Railway Operation Simulator  v2.19.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand() & random()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 //#include "DisplayUnit.h" included in TrackUnit.h
49 #include "PerfLogUnit.h"
50 #include "Utilities.h"
51 
52 // ---------------------------------------------------------------------------
53 #pragma package(smart_init)
54 
56 
57 // ---------------------------------------------------------------------------
58 
59 int TTrain::NextTrainID = 0; // has to be initialised outside the class
60 
61 // ---------------------------------------------------------------------------
62 
63 TExitInfo::TExitInfo() //default constructor
64 {
65  ServiceReference = " ";
66  RepeatNumber = 0;
67  TimeToExitSecs = -1;
68 }
69 
70 // ---------------------------------------------------------------------------
71 
72 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
73  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
74  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
75  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
76  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
77  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
78 /*
79  Construct a new train with general default values and input values for position and headcode.
80  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
81  This is because trains are kept in a vector and vectors erase elements during internal operations.
82  Deletion is explicit by using a special function. Increment the static class member NextTrainID
83  after setting this train's ID.
84 */
85 
86 {
87  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
88  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
89  AnsiString(TrainModeIn));
90  // AutoControl = true;//all trains start in auto control
91  UpdateCounter = 0;
92  TimeTimeLocArrived = false;
93  Derailed = false;
94  DerailPending = false;
95  Crashed = false;
96  StoppedAtBuffers = false;
97  StoppedAtSignal = false;
98  StoppedAtLocation = false;
99  StoppedAfterSPAD = false;
100  StoppedWithoutPower = false; // new at v2.4.0
101  StoppedForTrainInFront = false;
102  TrainInFront = false; //new at v2.18.0
103  SignallerStoppingFlag = false;
104  SignallerStopped = false;
105  SignallerRemoved = false;
106  NotInService = false;
107  HoldAtLocationInTTMode = false;
108  AllowedToPassRedSignal = false;
109  CallingOnFlag = false;
110  BeingCalledOn = false;
111  DepartureTimeSet = false;
113  TimetableFinished = false;
114  LastActionDelayFlag = false;
115  OneLengthAccelDecel = false;
116  TrainCrashedInto = -1;
118  Plotted = false;
119  TrainGone = false;
120  SPADFlag = false;
121  FrontCodePtr = new Graphics::TBitmap;
122  FrontCodePtr->PixelFormat = pf8bit;
123  FrontCodePtr->Height = 8;
124  FrontCodePtr->Width = 8;
126  FrontCodePtr->Transparent = false;
127  AValue = sqrt(2 * PowerAtRail / Mass);
129  TerminatedMessageSent = false;
130  JoinedOtherTrainFlag = false;
132  FollowOnServiceRef = ""; //added at v2.12.0
133  TreatPassAsTimeLocDeparture = false; //added at v2.12.0
134  StepForwardFlag = false;
136  for(int x = 0; x < 4; x++)
137  {
138  HeadCodeGrPtr[x] = new Graphics::TBitmap;
139  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
140  HeadCodeGrPtr[x]->Height = 8;
141  HeadCodeGrPtr[x]->Width = 8;
143  HeadCodeGrPtr[x]->Transparent = false;
144  }
145  for(int x = 0; x < 4; x++)
146  {
147  BackgroundPtr[x] = new Graphics::TBitmap;
148  BackgroundPtr[x]->PixelFormat = pf8bit;
149  BackgroundPtr[x]->Height = 8;
150  BackgroundPtr[x]->Width = 8;
152  BackgroundPtr[x]->Transparent = false;
153  }
154  for(int x = 0; x < 4; x++)
155  {
157  // set here to ensure have values
158  }
159  for(int x = 0; x < 4; x++)
160  {
161  PlotElement[x] = -1; // marker for not plotted yet
162  }
163  for(int x = 0; x < 3; x++)
164  {
165  OldZoomOutElement[x] = -1; // marker for not plotted yet
166  }
168  NextTrainID++;
169 
170  // new values added to complete initialisation of all TTrain variables
171 
172  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
173  // TrainDataEntryPtr, initialise in AddTrain
175  FrontElementLength = 0;
176  EntrySpeed = 0;
177  ExitSpeedHalf = 0;
178  ExitSpeedFull = 0;
179  MaxExitSpeed = 0;
180  BrakeRate = 0;
181  CoastingBrakeRate = 0.03; //added at v2.18.0
183  FirstHalfMove = true;
184  EntryTime = 0;
185  ExitTimeHalf = 0;
186  ExitTimeFull = 0;
187  ReleaseTime = 0;
188  TRSTime = 0;
189  LastActionTime = 0;
190  Straddle = MidLag;
191  LeadElement = -1;
192  LeadEntryPos = 0;
193  LeadExitPos = 0;
194  MidElement = -1;
195  MidEntryPos = 0;
196  MidExitPos = 0;
197  LagElement = -1;
198  LagEntryPos = 0;
199  LagExitPos = 0;
200  TrainFailed = false; // added at v2.4.0
201  for(int x = 0; x < 4; x++)
202  {
203  HOffset[x] = 0;
204  VOffset[x] = 0;
205  PlotEntryPos[x] = 0;
206  }
207  OpTimeToAct = 60; // default value, new at v2.2.0
208  TimeToExit = -1;
209  ExitPair.first = -1;
210  ExitPair.second = -1;
211  MinsDelayed = 0.0; // new at v2.2.0
212  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
213  FinishJoinLogSent = false;
214  // added at v2.4.0 to prevent repeatdly logging the event
217  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
221  ZeroPowerNoCDTMessage = false;
226  TrainFailurePending = false;
227  SkippedDeparture = false;
228  ActionsSkippedFlag = false;
229  SkipPtrValue = 0;
230  TrainSkippedEvents = 0;
231  DelayedRandMins = 0; //added at v2.13.0
232  NewDelay = 0; //added at v2.13.0
233  CumulativeDelayedRandMinsOneTrain = 0; //added at v2.13.0
234  ActualArrivalTime = TDateTime(0); //added at v2.13.0
235  LastSigPassedFailed = false; //added at v2.13.0
236  Utilities->CallLogPop(648);
237 }
238 
239 // ---------------------------------------------------------------------------
240 
241 void TTrain::DeleteTrain(int Caller)
242 /*
243  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
244  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
245  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
246  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
247  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
248  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
249  No need to delete HeadCodePosition as that just points to existing bitmaps
250 */{
251  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
252  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
253  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
254  if(Display->ZoomOutFlag)
255  {
257  }
258  if(FrontCodePtr == 0)
259  {
260  throw Exception("Error in attempting to delete FrontCodePtr");
261  }
262  delete FrontCodePtr;
263  FrontCodePtr = 0;
264  for(int x = 0; x < 4; x++)
265  {
266  if(BackgroundPtr[x] == 0)
267  {
268  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
269  }
270  delete BackgroundPtr[x];
271  BackgroundPtr[x] = 0;
272  }
273  for(int x = 0; x < 4; x++)
274  {
275  if(HeadCodeGrPtr[x] == 0)
276  {
277  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
278  }
279  delete HeadCodeGrPtr[x];
280  HeadCodeGrPtr[x] = 0;
281  }
282  Utilities->CallLogPop(649);
283 }
284 
285 // ---------------------------------------------------------------------------
286 
288 /*
289  Plots the train starting position on screen. Note that the check for starting on straight points &
290  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
291  ChangeDirection calls this function]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
292  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
293  Set the headcode graphics pointers from the headcode text, then check whether starting at a
294  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
295  for the continuation element. Otherwise set Lead and Mid values,
296 
297  and Lead element value unless
298  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
299  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
300  then check if a train on either Mid or Lag and if so give a warning message and return false so
301  that the calling function can delete the train. Plot the Mid element train values then do similarly
302  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
303  the train. Finally set the Plotted flag and return true.
304 */{
305  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
306  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
307 
309  // PlotStartTime = TrainController->TTClockTime;
310  FirstHalfMove = true;
311 
312  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
313  // 'claim' it for this train to prevent any other waiting trains trying to enter
315  {
316  LagElement = -1; // not to be plotted
317  LagExitPos = 0; // not to be plotted
318  LagEntryPos = 0; // not to be plotted
319  MidElement = -1; // not to be plotted
320  MidExitPos = 0; // not to be plotted
321  MidEntryPos = 0; // not to be plotted
323  LeadExitPos = 1; // will be 1 for continuation entry
324  LeadEntryPos = 0;
325 
327  MaxExitSpeed = StartSpeed; // initial value
329  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
330  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
331  if(EntrySpeed > SpeedLimit)
332  {
333  EntrySpeed = SpeedLimit;
334  }
336  {
338  }
340  // LeadElement is the element to be entered
341 
342  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
343  // can achieve ExitSpeedFull at the half braking rate.
345  {
346  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
347  if(TempEntrySpeed < EntrySpeed)
348  {
349  EntrySpeed = TempEntrySpeed;
351  }
352  }
353  Straddle = MidLag; // only for starting on a continuation
355  // no need to stop gap flashing if start on continuation
356  }
357  else // not starting at a continuation
358  {
359  LagElement = -1;
360  LagEntryPos = 0;
361  LagExitPos = 0;
368 
370  MaxExitSpeed = StartSpeed; // initial value
372  bool TempDerail = false; // dummy
373  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
375  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
376  {
377  StoppedWithoutPower = true;
378  }
379  // facing buffers check - ignore starting speed if start facing buffers
380  StoppedAtBuffers = false;
381  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
384  {
385  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
386  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
387  EntrySpeed = 0;
388  ExitSpeedHalf = 0;
389  ExitSpeedFull = 0;
390  MaxExitSpeed = 0;
391  // SetTrainMovementValues not called so set this here
392  BrakeRate = 0;
395  StoppedAtSignal = false;
396  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
397  // signal check is an 'else'
398  if(!StoppedAtLocation)
399  {
400  StoppedAtBuffers = true; // stopped at location takes precedence
401  }
402  }
403 
404  // facing continuation check - don't allow to stop even if no power
406  {
407  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
408  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
412  BrakeRate = 0;
413  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
414  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
415  }
416 
417  // Signal check
418  else if((NextElementPosition > -1) && (NextEntryPos > -1))
419  // condition check added as precaution after SloughIECC error reported by James U
420  {
421  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
422  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
423  {
424  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
425  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
426  EntrySpeed = 0;
427  ExitSpeedHalf = 0;
428  ExitSpeedFull = 0;
429  MaxExitSpeed = 0;
430  BrakeRate = 0;
433  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
434  {
435  StoppedAtSignal = true;
437  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
438  }
440  {
441  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass stop signal' is offered in popup menu rather than move
442  // forwards, but don't change the background colour so still shows as stopped at location
443  StoppedAtSignal = true;
444  }
445  }
446  else
447  {
448  StoppedAtSignal = false;
449  if(NextEntryPos > 1)
450  {
451  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
452  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
453  }
454  else
455  {
456  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
457  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
458  }
459  if(EntrySpeed > SpeedLimit)
460  {
461  EntrySpeed = SpeedLimit;
462  }
464  {
466  }
468  TDateTime TestTime = TrainController->TTClockTime; // test
469  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
470  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
471  // NextElement is the element to be entered
472 
473  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
474  // can achieve ExitSpeedFull at the half braking rate.
476  {
477  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
478  // half braking
479  if(TempEntrySpeed < EntrySpeed)
480  {
481  EntrySpeed = TempEntrySpeed;
482  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
483  }
484  }
485  }
486  }
488  {
489  throw Exception("Error, LeadElement Exit Connection is NotSet");
490  }
491  }
492  if(MidElement > -1) // will be -1 if start on continuation
493  {
494  Straddle = LeadMid;
498  {
499  for(int x = 0; x < 4; x++)
500  {
501  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
502  }
503  }
504  else
505  {
506  for(int x = 0; x < 4; x++)
507  {
509  }
510  }
511  if(TrainMode == Timetable)
512  {
514  }
515  else
516  {
518  }
520  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
521 
524 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
525  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
526  {
527  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
528  Utilities->CallLogPop(651);
529  return false;
530  }
531 */
536  PlotTrainGraphic(8, 0, Display);
537  PlotTrainGraphic(9, 1, Display);
538 
541 
542  // pick up background bitmaps [2] & [3]
543 
546 
547  PlotElement[2] = MidElement;
549  PlotElement[3] = MidElement;
551  PlotTrainGraphic(10, 2, Display);
552  PlotTrainGraphic(11, 3, Display);
553  // Plotted = true; set in PlotTrainGraphic
554  }
555  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
556  Utilities->CallLogPop(652);
557 }
558 
559 // ---------------------------------------------------------------------------
560 void TTrain::UnplotTrain(int Caller)
561 {
562  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
563  if(!Plotted)
564  {
565  return;
566  }
567  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
568 
569  if(Straddle == MidLag)
570  {
571  if(MidElement > -1)
572  {
577  // to force plot of locked route marker, needed once only for the element
578  }
579  if(LagElement > -1)
580  {
585  // to force plot of locked route marker, needed once only for the element
586  }
587  }
588  else if(Straddle == LeadMidLag)
589  {
590  if(LeadElement > -1)
591  {
594  // to force plot of locked route marker, needed once only for the element
595  }
596  if(MidElement > -1)
597  {
602  // to force plot of locked route marker, needed once only for the element
603  }
604  if(LagElement > -1)
605  {
608  // to force plot of locked route marker, needed once only for the element
609  }
610  }
611  else if(Straddle == LeadMid)
612  {
613  if(LeadElement > -1)
614  {
619  // to force plot of locked route marker, needed once only for the element
620  }
621  if(MidElement > -1)
622  {
627  // to force plot of locked route marker, needed once only for the element
628  }
629  }
630  if(LeadElement > -1)
631  {
633  }
634  if(MidElement > -1)
635  {
637  }
638  if(LagElement > -1)
639  {
641  }
642  Plotted = false;
644  Display->Update();
645  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
646  // resurrected when Update() dropped from PlotOutput etc
647  Utilities->CallLogPop(653);
648 }
649 
650 // ----------------------------------------------------------------------------
651 
652 void TTrain::UpdateTrain(int Caller)
653 /*
654  Note: Some changes made since comments written
655 
656  Brief:
657  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
658  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
659  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
660  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
661  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
662  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
663  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
664  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
665  changed to MidLag within the function and all elements moved down one, old Mid becomes
666  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
667  incremented to reflect the position the train now occupies.
668 
669  Detail:
670  Set TrainFailurePending if all conditions met
671  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
672  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
673  and return.
674  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
675  If there's a LagElement (there will be but include check for good practice - next
676  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
677  train fully on offending point - Derail set and DerailPanding reset, train background
678  colour changed (note that BackgroundColour is a property of the train itself) then return.
679  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
680  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
681  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
682  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
683  if LeadElement is a fouled trailing point.
684  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
685  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
686  replotting the last background segment and checking whether the element is a bridge or crossover with the other
687  track in a route, in which case the route colour is replotted.
688  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
689  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
690  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
691  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
692  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
693  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
694  train can be deleted by the calling function, and the function returns.
695  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
696  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
697  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
698  basic red aspect.
699 
700  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
701  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
702  regardless of direction, and with the correct front code colour.
703 
704  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
705  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
706  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
707  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
708  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
709  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
710  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
711 
712  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
713  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
714  changed similarly. The function then returns.
715 
716  If Crashed is not set then Straddle is incremented and the function returns.
717 */
718 
719 {
720  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
721  UpdateCounter++;
722  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
723  if(UpdateCounter >= 100)
724  {
725  UpdateCounter = 0;
726  }
727  int RandRange = (TrainController->MTBFHours * 3600) / 53;
728 
729  // MTBFHours is in timetable clock hours, min value is 1 & max value is 9,999 (integer values on input)
730  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
731  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
732  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
733  // RandomFailureCounter value is fixed for a full cycle of train updates so this
734  // makes sure there's no bunching of failures as there is for a fixed comparison number
735  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
736  // gives a random number between 0 and 16384 (defined as RAND_MAX in stdlib.h)
737  {
738  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
739  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
740  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
741  // don't fail if:
742  // (a) on a continuation (entering or leaving);
743  // (b) already failed;
744  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
745  // (d) train terminated;
746  // (e) crashed or derailed; or
747  // (f) under signaller control and stopped.
748  // (g) TreatPassAsTimeLocDeparture is true //added at v2.12.0
749  {
750  if((random(RandRange) == 0) && !TreatPassAsTimeLocDeparture)
751  // max value for RandRange is over 2x10^9
752  {
753  // here if failure due
754  TrainFailurePending = true;
755  // the failure occurs when PlotElements set to proper Lead & Mid Elements
756  }
757  }
758  }
759 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
760  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
761  {
762  StoppedWithoutPower = true;
763  }
764 */
765 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
766 // THVShortPair ExitPair; //added at v2.10.0
767  int LockedVectorNumber;
768  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
769  // default values - these needed for route checker below
770  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
771 
773  {
775  }
776  if(Crashed || Derailed)
777  {
779  {
780  PlotTrain(7, Display);
781  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
782  Display->Update();
783  }
784  OpTimeToAct = 0.0;
785  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
786  Utilities->CallLogPop(1017);
787  return; // no further action, user has to remove or work around
788  }
789 
791  {
793  }
795  {
797  }
798  if(StoppedWithoutPower && (TrainMode == Signaller)) //added at v2.13.2 as this condition not covered - was shown as normal
799  {
801  }
803  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
804  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
805  // to move & then stop again at the same station
806  {
807  TimeTimeLocArrived = false;
808  }
809  if(!Stopped() && !SPADFlag && !TrainFailed)
810  {
812  }
813  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
814  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
815 /* old version where force a stop at buffers regardless of speed
816  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
817  else StoppedAtBuffers = false;
818 */
819 
820  // new version where crash if run into buffers
821  if(!Crashed)
822  {
823  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
824  {
825  if(ExitSpeedFull > 1)
826  {
827  Crashed = true;
831  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
832  // no need for missed action logs - will be sent when train removed
833  StoppedAtBuffers = false;
834  }
836  // stopped at location & stopped without power take precedence
837  {
838  StoppedAtBuffers = true;
839  }
840  else
841  {
842  StoppedAtBuffers = false;
843  }
844  }
845  else
846  {
847  StoppedAtBuffers = false;
848  }
849  }
850  else
851  {
852  StoppedAtBuffers = false;
853  }
854  // if crashed don't want stopped at buffers set
855 
856  // also crash if run into a level crossing that is changing or has barriers up
857  if(!Crashed)
858  {
859  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
860  {
861  int H = Track->TrackElementAt(873, LeadElement).HLoc;
862  int V = Track->TrackElementAt(874, LeadElement).VLoc;
863  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
864  {
865  Crashed = true;
869  // no need for missed action logs - will be sent when train removed
870  }
871  }
872  }
874  {
876  }
877  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
879  //if Command == "" then either TimeLoc or TimeTimeLoc so don't hold, and added last part at v2.12.0 so don't hold if have both command == pas and Treat... flag
880  {
881  HoldAtLocationInTTMode = true;
882  }
883  else if(TrainMode == Timetable)
884  {
885  HoldAtLocationInTTMode = false;
886  }
887  // in Signaller mode HoldAtLocationInTTMode not changed
888 
889  // check if departure pending & set times unless already set
890  if(TrainMode == Timetable)
891  {
893  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
894  {
895  if((ActionVectorEntryPtr->Command != "pas") && (ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) && (ActualArrivalTime > TDateTime(0)))
896  {
897  AnsiString ReasonArray[24] = {"a driver is awaited","a guard is awaited","of a medical emergency","of a technical problem","of a security issue",
898  "of a safety issue","of a disturbance","a train crew member has been taken ill","the driver has been taken ill","the guard has been taken ill",
899  "a report has been received concerning safety","a shoe has been lost under the train","of a reported theft",
900  "of an incident involving an animal","some luggage has been lost under the train","a minor repair is needed","a suspicious object has to be dealt with safely",
901  "a door is stuck open","additional stock has to be attached","a security alert","of a train fault","of an operating incident","safety checks are required",
902  "of a shortage of on train crew"};
903  //(ActionVectorEntryPtr->Command != "pas") added at v2.13.0 to rule out passes, though probably not needed
904  //(ActualArrivalTime > TDateTime(0)) added at v2.13.0 to ensure that it has been set and to dismiss trains that are present
905  //at start or have no departure time set.
906  NewDelay = 0; //section relating to random delays added at v2.13.0
907  TDateTime TimetableReleaseTime = TrainController->GetRepeatTime(0, ActionVectorEntryPtr->DepartureTime, RepeatNumber, IncrementalMinutes); //Timetable value
908  TDateTime DwellTime = TimetableReleaseTime - ActualArrivalTime; //Timetable value
909  if(DwellTime < TDateTime(30.0 / 86400))
910  {
911  DwellTime = TDateTime(30.0 / 86400);
912  }
913  int randval = random(10000);
914  if(randval != 0) //if randval == 0 or DelayMode == Nil then NewDelay will be 0 as set above
915  {
916  if(Utilities->DelayMode == Minor)
917  {
918  if(randval < Utilities->MinorDelayCutoff)
919  {
920  NewDelay = Utilities->MinorDelayFactor * log(Utilities->MinorDelayCutoff/randval);//minutes (confusingly log in C++Builder gives the natural logarithm)
921  }
922  }
923  else if(Utilities->DelayMode == Moderate)
924  {
925  if(randval < Utilities->ModerateDelayCutoff)
926  {
928  }
929  }
930  else if(Utilities->DelayMode == Major)
931  {
932  if(randval < Utilities->MajorDelayCutoff)
933  {
935  }
936  }
937  }
938 //NewDelay = 25; //test
939  if(double(TrainController->TTClockTime) <= (Utilities->LastDelayTTClockTime + 5.0/1440.0)) //if within 5 mins of last delay for any train
940  { //then don't delay. Added at v2.13.0
941  NewDelay = 0;
942  }
943  if(NewDelay < 1)
944  {
945  NewDelay = 0;
946  }
947  if(NewDelay < double(DwellTime) * 1440) //if less than scheduled dwell time then no additional delay
948  {
949  NewDelay = 0;
950  }
951  else
952  {
953  NewDelay -= double(DwellTime) * 1440;//reduce delay by dwell time
954  }
955  if(DelayedRandMins > 0)
956  {
957  DelayedRandMins -= double(DwellTime) * 1440;//reduce knock-on random delay by dwell time
958  }
959  if(DelayedRandMins < 0)
960  {
961  DelayedRandMins = 0;//can't be less than zero
962  }
964  {
965  NewDelay -= DelayedRandMins; //NewDelay is the additional delay over and above the existing knock-on delay (from earlier random delays)
966  //the formula above already includes knock-on effects
967  DelayedRandMins += NewDelay; //the new total delay, knock-on + additional
968 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //don't add here, add when depart, else this value can be > late mins
969  }
970  else
971  {
972  NewDelay = 0;
973 // CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //as above
974  }
975  ReleaseTime = LastActionTime + TDateTime(NewDelay / 1440); //earliest possible release time
976  if(NewDelay < 0.5) //less than the 30 secs min interval
977  {
978  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
979  }
980  if(ReleaseTime < TimetableReleaseTime)
981  {
982  ReleaseTime = TimetableReleaseTime; //back to correct time
983  NewDelay = 0;
984  DelayedRandMins = 0;
985  }
986  if(DelayedRandMins > double(ReleaseTime - TimetableReleaseTime) * 1440)
987  {
988  DelayedRandMins = double(ReleaseTime - TimetableReleaseTime) * 1440; //reduce this if time has been made up
989  }
990 
991  if(DelayedRandMins < NewDelay) //may be if reduced above, but if so need to reduce NewDelay also
992  {
994  }
995  //may be possible to simplify all the above but as it seems to work ok leave as is
996  if(int(NewDelay) > 0) //additional delay over and above knock-on effects from earlier random delays
997  {
999  if(int(NewDelay) == 1)
1000  {
1001  Display->WarningLog(12, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " delayed at " +
1002  ActionVectorEntryPtr->LocationName + " by 1 minute");
1003  PerfLogForm->PerformanceLog(18, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: " + HeadCode + " delayed at " +
1004  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1005  TrainController->StopTTClockMessage(140, HeadCode + " delayed at " +
1006  ActionVectorEntryPtr->LocationName + " by 1 minute because of a minor technical issue");
1007  }
1008  else
1009  {
1010  Display->WarningLog(11, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " delayed at " +
1011  ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) + " minutes");
1012  if(NewDelay >= 10) //give variable reasons for >= 10 mins
1013  {
1014  int randval2 = rand() % 24; //24 reasons
1015  AnsiString Reason = ReasonArray[randval2];
1017  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1018  " minutes because " + Reason);
1019  TrainController->StopTTClockMessage(141, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1020  " minutes because " + Reason);
1021  }
1022  else
1023  {
1025  HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1026  " minutes because of a minor problem");
1027  TrainController->StopTTClockMessage(142, HeadCode + " delayed at " + ActionVectorEntryPtr->LocationName + " by " + AnsiString(int(NewDelay)) +
1028  " minutes because of a minor problem");
1029  }
1030  }
1031  }
1032  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1033  ActualArrivalTime = TDateTime(0); //only run through this section once per arrival
1034  DepartureTimeSet = true;
1035  }
1036  else if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1)) //as was, for trains that don't have an errival time set
1037  {//if have skipped to a new service then DepartureTime will be set (in above segement when earlier train arrived)
1038  //but ArrivalTime won't be set as it is reset to 0 at end of above segement when earlier train arrived, so this segement
1039  //will run without any random delays which might cause additional complications from mixing modifications and best avoided.
1040  NewDelay = 0;
1041  DelayedRandMins = 0;
1043  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1044  {
1045  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1046  }
1047  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1048  DepartureTimeSet = true;
1049  }
1050  else if((ActionVectorEntryPtr->Command == "pas") && TreatPassAsTimeLocDeparture) //new segment at v2.12.0 to treat a pass as a departure
1051  {//for when skip to a new service at a pass location. As above this also avoids any random delays, and will avoid above segment because
1052  //departure time isn't set - it's an event time. Again random delays in this situation might cause additional complications
1053  //from mixing modifications so best avoided.
1054  NewDelay = 0;
1055  DelayedRandMins = 0;
1057  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
1058  {
1059  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
1060  }
1061  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
1062  DepartureTimeSet = true;
1063  }
1064  }
1065  }
1066  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
1067  {
1068  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
1069 // this->TimeToExit = TimeToExit; don't need these as values updated directly
1070 // this->ExitPair = ExitPair;
1071  // calculate every 1 sec (in real time, not timetable time) for all trains
1072  }
1073  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
1074  if(TrainMode == Timetable)
1075  {
1076  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
1077  {
1078  RemainHereLogNotSent = true;
1079  }
1081  {
1082  // ignore TimeLoc & TTLoc departures
1083  // Action logs given in functions
1085  LastActionTime + TDateTime(30.0 / 86400)))
1086  {
1087  if(ActionVectorEntryPtr->Command == "fsp")
1088  {
1089  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to
1090  //'this' train. Next clock cycle will deal with any required changes
1091  FrontTrainSplit(0);
1092  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1093  { //this is checked before each return
1094  TrainHasFailed(0);
1095  }
1096  Utilities->CallLogPop(2041);
1097  return;
1098  }
1099  else if(ActionVectorEntryPtr->Command == "rsp")
1100  {
1101  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to
1102  //'this' train. Next clock cycle will deal with any required changes
1103  RearTrainSplit(0);
1104  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1105  {
1106  TrainHasFailed(1);
1107  }
1108  Utilities->CallLogPop(2042);
1109  return;
1110  }
1111  else if(ActionVectorEntryPtr->Command == "Fjo")
1112  {
1113  FinishJoin(0);
1114  }
1115  else if(ActionVectorEntryPtr->Command == "jbo")
1116  {
1117  JoinedBy(0);
1118  }
1119  else if(ActionVectorEntryPtr->Command == "cdt")
1120  {
1121  ChangeTrainDirection(0, false);
1122  }
1123  else if(ActionVectorEntryPtr->Command == "dsc")
1124  {
1125  Description = ActionVectorEntryPtr->NewDescription; //changed at v2.16.1
1129  }
1130  else if(ActionVectorEntryPtr->Command == "Fns")
1131  {
1132  NewTrainService(0, false);
1133  }
1134  else if(ActionVectorEntryPtr->Command == "Frh")
1135  {
1136  RemainHere(0);
1137  }
1138  else if(ActionVectorEntryPtr->Command == "Fer")
1139  {
1140  TimetableFinished = true;
1141  }
1142  // other aspects of 'Fer' dealt with in TTrain::HasTrainGone()
1143  else if(ActionVectorEntryPtr->Command == "F-nshs")
1144  {
1146  }
1147  else if(ActionVectorEntryPtr->Command == "Frh-sh")
1148  {
1149  RepeatShuttleOrRemainHere(0, false);
1150  }
1151  else if(ActionVectorEntryPtr->Command == "Fns-sh")
1152  {
1154  }
1155 /*
1156  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
1157  shuttle headcode (no train creation)
1158  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1159  remain here
1160  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
1161  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
1162 */
1163  }
1164  }
1165  else
1166  {
1168  {
1170  }
1171  }
1172  }
1173  if(TrainMode == Timetable)
1174  {
1175  if(StoppedAtBuffers)
1176  {
1177  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
1178  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
1179  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
1180  if(BufferLocation == "")
1181  {
1183  }
1184  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1185  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1186  {
1190  {
1192  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1193  // Drop missed actions so user can still use sig mode to get back on track
1195  }
1196  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1197  {
1199  TrainHasFailed(2);
1200  }
1201  Utilities->CallLogPop(1020);
1202  return;
1203  }
1204  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !RevisedStoppedAtLoc() && (TrainController->TTClockTime >
1205  ReleaseTime))
1206  {
1209  {
1212  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1213  // Drop missed actions so user can still use sig mode to get back on track
1215  }
1216  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1217  {
1219  TrainHasFailed(3);
1220  }
1221  Utilities->CallLogPop(1397);
1222  return;
1223  }
1224  }
1225  else
1226  {
1228  }
1229  }
1230  else
1231  {
1233  }
1234  if(TrainMode == Timetable)
1235  {
1237  {
1239  }
1241  {
1243  }
1244  }
1245  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1246  // restart after stopped for train in front
1247  int NextElementPosition, NextEntryPos;
1248 
1249  if(LeadElement > -1) // if an exit continuation then not set
1250  {
1251  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1252  {
1254  }
1255  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1256  {
1257  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1258  {
1259  LeadExitPos = 1;
1260  }
1261  else
1262  {
1263  LeadExitPos = 3;
1264  }
1265  }
1266  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1267  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1268  }
1269  else
1270  {
1271  NextElementPosition = -1;
1272  NextEntryPos = -1;
1273  }
1274  if((NextElementPosition > -1) && (NextEntryPos > -1))
1275  // may be buffers or continuation so need this check
1276  {
1277 /*
1278  Check whether calling-on conditions met:-
1279  a) approaching train has stopped at a signal but not at a location;
1280  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1281  change of direction (cdt), remaining here (Frh), or under signaller control);
1282  c) at least 1 platform available for the approaching train;
1283  d) points (if any) set for direct route into platform;
1284  e) approaching train is to stop at station;
1285  f) no more facing signals between train and platform;
1286  g) [dropped g]
1287  h) train in front preventing route being set far enough to release stop signal;
1288  i) train in front not exiting at continuation;
1289  j) signal must be within 4km of the stop platform;
1290  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1291  l) no existing route conflicts with the route into the platform; and
1292  m) not failed or without power (these added at v2.10.0)
1293  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1294  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1295 */
1296  if(TrainMode == Timetable)
1297  {
1298  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1299  {
1300  CallingOnFlag = true;
1301  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1302  }
1303  else
1304  {
1305  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1306  {
1307  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1308  {
1310  }
1311  }
1312  CallingOnFlag = false;
1313  }
1314  }
1315  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !RevisedStoppedAtLoc())
1316  {
1317  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1318  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1319  // sets StoppedAtSignal again & train doesn't move
1320  StoppedAtSignal = false;
1321  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1322  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1323  // LeadMidLag and front of train was on LeadElement (after the current move)
1325  EntrySpeed = 0;
1327  FirstHalfMove = true;
1328  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1329  // NextElement is the element to be entered
1330  }
1331  if((LeadElement > -1) && (LeadExitPos > -1))//this section added at v2.18.0
1332  {
1333  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1334  if(NextPos > -1)
1335  {
1336  int NextEntryPos = Track->TrackElementAt(1674, LeadElement).ConnLinkPos[LeadExitPos];
1337  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1338  // true if another train on NextEntryPos track whether bridge or not
1339  {
1340  TrainInFront = true;
1341  }
1342  else
1343  {
1344  TrainInFront = false;
1345  }
1346  }
1347  }
1349  {
1350  if(ClearToNextSignal(0))
1351  {
1352  StoppedForTrainInFront = false;
1353  TrainInFront = false;
1354  BeingCalledOn = false;
1355  EntrySpeed = 0;
1357  FirstHalfMove = true;
1358  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1359  }
1360  }
1361  }
1362  if(Stopped() && TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1363  {
1364  TrainHasFailed(4);
1365  Utilities->CallLogPop(1097);
1366  return;
1367  }
1368  if((Straddle == MidLag) && (LeadElement != -1))
1369  // later check only for Straddle == LeadMid, so need this check here for initial train start
1370  {
1372  }
1373 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1374  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1375  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1376  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1377  which could be when start as Snt.
1378  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1379  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1380  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1381  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1382  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1383  reached.
1384  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1385  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1386  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1387  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1388  sending a message to the performancelog.
1389 */
1390 
1391  if(TrainMode == Timetable)
1392  {
1394  {
1395 // if(BeingCalledOn) //dropped when added TrainInFront at v2.18.0
1396 // {
1397 // TrainInFront = true;
1398 // }
1400  {
1402  }
1403  else //added at v2.14.0 as if a train ready to depart (pink b'gnd) taken under sig control then restored to tt control b'gnd stayed pink,
1404  { //even though release time now 30 seconds after tt control restored
1406  }
1408  {
1409  // value updated at every scheduled departure & arrival
1411  AnsiString StationName;
1413  {
1415  }
1417  {
1419  }
1420  else
1421  {
1422  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1423  }
1424  EntrySpeed = 0;
1425  bool Derail; //not used
1427  int NextElementPosition = Track->TrackElementAt(199, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(8, LeadElement, LeadEntryPos, Derail)];
1429  FirstHalfMove = true; //above changed at 2.18.0 from GetNonPoints... to GetAnyElement... as had wrong
1430  StoppedAtLocation = false; //element and link found with non-station names on points as can now stop on points
1431 
1432  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1433  {
1434  StoppedWithoutPower = true;
1435  }
1436  if((NextElementPosition > -1) && (NextEntryPos > -1))
1437  // condition check added for SloughIECC error reported by James U
1438  {
1439  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1440  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1441  {
1442  StoppedAtSignal = true;
1443  if(!StoppedWithoutPower)
1444  // if stopped without power just keep existing background colour
1445  {
1447  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1448  }
1449  }
1450  }
1452  {
1453  TimeTimeLocArrived = false;
1454  LogAction(27, HeadCode, "", Depart, StationName, "", ActionVectorEntryPtr->DepartureTime, false);
1455  // no warning for TimeTimeLoc departure
1456  }
1457  else if(TreatPassAsTimeLocDeparture) //added at v2.12.0 so late/early/on time mins recorded accurately
1458  {
1459  LogAction(36, HeadCode, "", Depart, StationName, "", ActionVectorEntryPtr->EventTime, ActionVectorEntryPtr->Warning); //EventTime because the real event is a pass
1460  }
1461  else //must be TimeLoc departure
1462  {
1464  }
1465  TreatPassAsTimeLocDeparture = false; //added at v2.12.0, reset after train departs
1466  DepartureTimeSet = false;
1467  // no need to set LastActionTime for a departure
1468  //deal here with departure pointer change, increment if SkippedDeparture
1469  CumulativeDelayedRandMinsOneTrain += DelayedRandMins; //only add these after late mins added (in LogAction)
1470 
1471  if(SkippedDeparture)
1472  {
1475  TrainSkippedEvents = 0;
1476  SkippedDeparture = false;
1477  SkipPtrValue = 0;
1478  ActionsSkippedFlag = false;
1479  }
1480  else
1481  {
1483  }
1484  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1485  // note
1486 /*
1487  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1488  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1489  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1490  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1491  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1492  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1493  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1494 */
1496  {
1497  StoppedAtBuffers = true;
1498  }
1499  else if(!StoppedWithoutPower)
1500  // if buffers or no power, don't set values
1501  {
1503  {
1504  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1505  // NextElement is the element to be entered
1506  }
1507  else
1508  {
1510  // use LeadElement for an exit continuation
1511  }
1512  }
1513  }
1514  }
1515  }
1516  if(Straddle == LeadMidLag) //train on a half element
1517  {
1519  {
1520  Utilities->CallLogPop(654);
1521  return;
1522  }
1523  }
1524  else //train fully on 2 elements
1525  {
1527  {
1528  Utilities->CallLogPop(655);
1529  return;
1530  }
1531  }
1532  if((LeadElement > -1) && (MidElement > -1))
1533  {
1535  {
1536  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1537  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1538  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1539  SignallerStoppingFlag = false;
1540  StepForwardFlag = false;
1541  }
1542  }
1543  if(Stopped())
1544  // this is what prevents another movement if the train is stopped
1545  {
1546  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
1547  {
1548  TrainHasFailed(5);
1549  }
1550  BrakeRate = 0;
1551  Utilities->CallLogPop(656);
1552  return;
1553  }
1554 
1555  // HERE WHEN READY FOR NEXT MOVE
1556 
1557  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1558  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1559  if(Straddle == LeadMid) //fully on 2 elements
1560  {
1561  if(LeadElement > -1)
1562  {
1563  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1564  {
1566  if(TIF.TrackType == SignalPost)
1567  {
1568  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1569  int TIFExitPos = 0;
1570  if(TIFEntryPos == 0)
1571  {
1572  TIFExitPos = 1;
1573  }
1574  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal && !TIF.CallingOnSet) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1576  {
1577  SPADFlag = true; // user has to intervene to reset & restart after spad
1578  }
1579  }
1580  }
1581  }
1582  }
1583 
1584  // check for train in front & if so stop at next access (when train fully on element next to train)
1585  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1586  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1587  // variable TrainInFrontInSignallerModeFlag
1588  {
1589  if((LeadElement > -1) && (LeadExitPos > -1))
1590  {
1591  int NextPos = Track->TrackElementAt(1672, LeadElement).Conn[LeadExitPos];
1592  int NextEntryPos = Track->TrackElementAt(1675, LeadElement).ConnLinkPos[LeadExitPos];
1593  if(Track->OtherTrainOnTrack(16, NextPos, NextEntryPos, TrainID))
1594  // true if another train on NextEntryPos track whether bridge or not
1595  {
1596  TrainInFront = true;
1597  }
1598  else
1599  {
1600  TrainInFront = false;
1601  }
1602  }
1603  }
1604  if((Straddle == LeadMid) && SPADFlag)
1605  // give message + plot background when ready to move half past the signal
1606  {
1607  if(NextElementPosition > -1)
1608  {
1609  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1610  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1611  {
1612  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1614  // if goes past 2 signals then give message twice
1616  }
1617  }
1618  }
1619  if(Straddle == LeadMidLag)
1620  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1621  {
1622  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1623  if(SPADFlag)
1624  {
1625  if(ExitSpeedFull == 0)
1626  {
1627  StoppedAfterSPAD = true;
1628  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1629  }
1630  }
1632  {
1633  if(ExitSpeedFull == 0)
1634  {
1635  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1636  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1637  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1638  // is sent at the right time and once only.
1639  SignallerStopped = true;
1640  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1641  StepForwardFlag = false;
1642  SignallerStoppingFlag = false;
1643  TTrackElement TE;
1644  AnsiString Loc = "";
1645  bool LocNamed = false;
1646  if(LeadElement > -1)
1647  {
1648  TE = Track->TrackElementAt(782, LeadElement);
1649  if(TE.ActiveTrackElementName != "")
1650  {
1651  Loc = TE.ActiveTrackElementName;
1652  LocNamed = true;
1653  }
1654  else
1655  {
1656  Loc = "track element " + TE.ElementID;
1657  }
1658  }
1659  if((MidElement > -1) && !LocNamed)
1660  {
1661  TE = Track->TrackElementAt(783, MidElement);
1662  if(TE.ActiveTrackElementName != "")
1663  {
1664  Loc = TE.ActiveTrackElementName;
1665  LocNamed = true;
1666  }
1667  else if(Loc == "")
1668  {
1669  Loc = "track element " + TE.ElementID;
1670  }
1671  }
1672  if(Loc == "")
1673  {
1674  Loc = "outside railway";
1675  // must have stopped after left at a continuation (because both lead & mid == -1)
1676  }
1677  else
1678  {
1679  Loc = "at " + Loc;
1680  }
1681  LogAction(30, HeadCode, "", SignallerStop, Loc, "", TrainController->TTClockTime, false); // false for warning
1682  }
1683  }
1684  if(LeadElement > -1) // if an exit continuation then not set
1685  {
1686  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1687  {
1689  }
1690  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1691  {
1692  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1693  {
1694  LeadExitPos = 1;
1695  }
1696  else
1697  {
1698  LeadExitPos = 3;
1699  }
1700  }
1701  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1702  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1703  }
1704  else
1705  {
1706  NextElementPosition = -1;
1707  NextEntryPos = -1;
1708  }
1711  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1712 
1713  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1714  {
1715  StoppedWithoutPower = true;
1716  }
1717  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1718  // may be buffers or continuation. SPADFlag added at v2.1.0
1719  // so don't override the SPAD colour & don't set StoppedAtSignal
1720  {
1721  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1722  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !RevisedStoppedAtLoc())
1723  {
1724  StoppedAtSignal = true;
1725  if(!StoppedWithoutPower)
1726  // leave background as is if no power, but set StoppedAtSignal
1727  {
1729  }
1730  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1731  }
1732  }
1733  if(!Stopped())
1734  {
1735  if((NextElementPosition > -1) && (NextEntryPos > -1))
1736  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1737  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1738  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1739  // function for fuller explanation
1740  {
1741  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1742  // NextElement is the element to be entered
1743  }
1744  // follow the continuation exits:-
1745  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1746  {
1748  // Use LeadElement for calcs if lead is a continuation
1749  }
1750  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1751  {
1753  // Use MidElement for calcs if mid is a continuation
1754  }
1755  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1756  {
1758  // Use LagElement for calcs if lag is a continuation
1759  }
1760  }
1761  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1762  if((AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute))
1763  // Trains may not be in a route
1764  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1765  {
1766  // NB if LeadElement == -1 then the above test returns NoRoute
1767  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1768  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1769  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1770  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1771  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1772  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1773  FirstPair.second).GetELink() == TempELink))
1774  {
1775  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1776  }
1777  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1778  SecondPair.second).GetELink() == TempELink))
1779  {
1780  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1781  }
1782  }
1783  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1784  // Trains may not be in a route
1785  {
1786  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1787  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1788  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1789  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1790  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1791  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1792  FirstPair.second).GetELink() == TempELink))
1793  {
1794  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1795  }
1796  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1797  SecondPair.second).GetELink() == TempELink))
1798  {
1799  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1800  }
1801  }
1802  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1803  // Trains may not be in a route
1804  {
1805  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1806  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1807  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1808  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1809  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1810  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1811  FirstPair.second).GetELink() == TempELink))
1812  {
1813  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1814  }
1815  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1816  SecondPair.second).GetELink() == TempELink))
1817  {
1818  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1819  }
1820  AllRoutes->CheckMapAndRoutes(8); // test
1821  }
1822  if(LagElement > -1)
1823  // not entering at a continuation so can deal with train leaving the lag element
1824  {
1826  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1827  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1828  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1829 
1830  TPrefDirElement PrefDirElement;
1831  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1832  // as this is a 16x16 graphic
1834  {
1836  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1837  }
1838  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1839  {
1840  int RouteNumber;
1841  TrainGone = true;
1842  // flag to indicate train to be deleted - outside this function
1844  {
1845  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1846  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1847  // calc distance from & inc last signal to exit
1848  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1849  int NewLastElement = 0, NewLastExitPos = 0;
1850  // need above because can't change LastElement & LastExitPos until both new values obtained
1851  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1852  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1853  LastElement).TrackType != Points))
1854  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1855  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1856  // leave CumDistance as it was in these circumstances.
1857  {
1858  if(LastExitPos < 2)
1859  {
1860  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1861  }
1862  else
1863  {
1864  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1865  }
1866  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1867  if(NewLastElement == -1)
1868  // this will catch buffers or any other connection failure
1869  {
1870  break; //throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain"); //dropped at v2.15.0 because of Brent Mackie's error file of
1871  } //10/02/23, had two continuations linked with no signal between
1872  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)]; //so when train exited this routine tracked
1873  if(NewLastExitPos == -1) //back to the entry continuation which had no further connection - doesn't need to be an error at all!
1874  {
1875  break; //throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain"); //dropped at v2.15.0 because of Brent Mackie's error file of 10/02/23 , see above
1876  }
1877  LastElement = NewLastElement;
1878  LastExitPos = NewLastExitPos;
1879  }
1880  // if at signal add this in too (may not be signal if 'break;' encountered but doesn't matter)
1881  if(CumDistance < 1200)
1882  {
1883  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1884  }
1885  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1886  // else use 1200m - CumDistance
1887  int FirstDistance = 0;
1888  if(CumDistance >= 1200)
1889  {
1890  FirstDistance = 100;
1891  }
1892  else
1893  {
1894  FirstDistance = 1200 - CumDistance;
1895  }
1896  if(FirstDistance < 100)
1897  {
1898  FirstDistance = 100; // don't allow < 100
1899  }
1900  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1901  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1902  if(ExitSpeedFull > 20.0)
1903  {
1904  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1905  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1906  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1907  // 4320.0 = 3.6 * 1200, .0 to make it a double
1908  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1909  }
1910  else
1911  {
1912  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs between each action
1913  ContinuationAutoSigEntry.SecondDelay = 120.0;
1914  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1915  }
1916  ContinuationAutoSigEntry.AccessNumber = 0;
1917  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1919  {
1921  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1922  VectorIT++)
1923  {
1924  if(VectorIT->RouteNumber == RouteNumber)
1925  {
1926  // another train has passed out of same route so erase earlier entry
1927  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1928  break;
1929  }
1930  }
1931  }
1932  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1933  }
1935  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1936  Display->Update();
1937  // need to keep this since Update() not called for PlotSmallOutput as too slow
1938  Utilities->CallLogPop(659);
1939  return;
1940  }
1941  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1942  if(LeadElement > -1)
1943  {
1944  TTrackElement &TE = Track->TrackElementAt(224, LeadElement); //added at v2.13.0 for brevity
1945  if(TE.Config[LeadExitPos] == Signal)
1946  // changed to lead so reset early
1947  {
1948  LastSigPassedFailed = false; //used to cancel route elements up to next signal for autosigs route
1949  TE.Attribute = 0; // red
1950  int RouteNumber; //only used for autosigs routes
1951  //add chance to fail when train passes a signal
1952  if((random(Utilities->SignalChangeEventsPerFailure) == 0) && !TE.Failed && (Utilities->FailureMode != FNil) &&
1953  (TrainMode == Timetable) && !TE.CallingOnSet) //can't fail twice, calling on signal can't fail
1954  {
1956  IFE.TVPos = LeadElement;
1957  TE.Failed = true;
1958  Display->WarningLog(19, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": Signal failed at " + TE.ElementID);
1959  PerfLogForm->PerformanceLog(42, Utilities->Format96HHMMSS(TrainController->TTClockTime) + " WARNING: Signal failed at " + TE.ElementID);
1960  TrainController->StopTTClockMessage(129, "Signal at " + TE.ElementID +
1961  " failed when changing aspect.\nTrains can only pass under signaller control.");
1962  AllRoutes->RebuildRailwayFlag = true; //force ClearandRebuildRailway at next clock tick
1963  LastSigPassedFailed = true;
1964  //set repair time, random value in minutes between 10 and 179
1965  double FailureMinutes = double(random(Utilities->MaxRandomRepairTime) + Utilities->FixedMinRepairTime); //between 10 and 179 minutes at random
1966  TDateTime RepairTime = TrainController->TTClockTime + TDateTime(FailureMinutes / 1440);
1967  IFE.RepairTime = RepairTime;
1969  Track->FailedSignalsVector.push_back(IFE); //rearwards signals will be set when LagElement leaves signal
1970  }
1971  TE.CallingOnSet = false;
1972  // don't plot if zoomed out
1973  if(!Display->ZoomOutFlag)
1974  {
1976  }
1977  // covers signal resetting in same direction
1978  }
1979  }
1981  {
1982  AllRoutes->RebuildRailwayFlag = true; //added at v2.13.0 to replot signal after train left in case it had failed
1983  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1984  {
1985  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1986  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1987  TPrefDirElement PrefDirElement;
1988  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1990  {
1992  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1993  }
1995  LockedVectorNumber)))
1996  {
1998  }
1999  }
2000  }
2001  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
2002  {
2003  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
2005  // don't plot if zoomed out
2006  if(!Display->ZoomOutFlag)
2007  {
2009  }
2010  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
2011  }
2013  {
2014  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
2015  {
2016  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
2017  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
2018  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
2019  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
2020  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
2021  int RouteNumber;
2023  // already know it's an autosigsroute, this is just to get the RouteNumber
2024  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
2025  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
2026  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
2027  int RouteNumber2;
2029  // already know it's an autosigsroute, this is just to get the RouteNumber
2030  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
2031  // note that if not in a route (as likely) then RouteNumber2 set to -1
2032  {
2033  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
2034  // this was in the 1.3.0 addition but without the condition
2035  }
2036  // end of 1.3.2 addition
2037  // end of 1.3.0.addition
2038  }
2039  TPrefDirElement PrefDirElement;
2040  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
2042  {
2044  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
2045  }
2046  }
2047  }
2048  }
2049  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
2050  if(Straddle == LeadMid)
2051  {
2052  AllowedToPassRedSignal = false;
2053  // if had been allowed to pass then at this point it will move half onto signal so can be reset
2054  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
2055  if(DerailPending)
2056  // set during last GetLeadElement, but only act on it when train fully on offending point
2057  // i.e. next time Straddle reaches LeadMid
2058  {
2059  Derailed = true;
2060  DerailPending = false;
2064  Utilities->CallLogPop(657);
2065  return;
2066  }
2073  Straddle = MidLag;
2074  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
2075  // LeadElement during this function (note that if stopped at signal then won't get this far)
2076  if(LeadElement > -1)
2077  {
2079  // i.e an exit continuation only
2080  // if don't exclude entry continuations then can't progress past it
2081  {
2082  LeadElement = -1;
2083  }
2084  else
2085  {
2086  GetLeadElement(0);
2087  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
2089  if(Stopped())
2090  {
2091  if(TrainFailurePending) // ok, stopped so PlotElements set when the train stopped in an earlier update
2092  {
2093  TrainHasFailed(6);
2094  }
2095  Utilities->CallLogPop(658);
2096  return; // i.e. don't move forward one step if next element is a red signal
2097  }
2098  }
2099  }
2100  }
2101  if(LagElement > -1)
2102  {
2103  // below are the actions required at both half moves for LagElement > -1
2105 
2106  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
2107  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
2108  // need to do this for each half element
2109 
2110  TPrefDirElement PrefDirElement;
2111  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
2112  {
2113  int RouteNumber; // holder for call below - not used
2115  {
2116  if(Utilities->clTransparent == TColor(0xFFFFFF))
2117  // change to black for a white background
2118  {
2120  // only applies for AutoSigs Route in case was locked & timed out
2121  }
2122  else
2123  // change to white for a dark background
2124  {
2126  // only applies for AutoSigs Route in case was locked & timed out
2127  }
2129  }
2130  }
2132  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
2133  // or a train on the opposite track - needs to be replotted
2134  }
2135  // update all array values
2136  HOffset[3] = HOffset[2];
2137  HOffset[2] = HOffset[1];
2138  HOffset[1] = HOffset[0];
2139  VOffset[3] = VOffset[2];
2140  VOffset[2] = VOffset[1];
2141  VOffset[1] = VOffset[0];
2142  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
2143 
2144  BackgroundPtr[3] = BackgroundPtr[2];
2145  BackgroundPtr[2] = BackgroundPtr[1];
2146  BackgroundPtr[1] = BackgroundPtr[0];
2147  BackgroundPtr[0] = TempPtr;
2148 
2149  // update headcode graphics depending on Lead entry value
2150  if(LeadElement > -1) // if Lead is -1 then stays as is
2151  {
2153  {
2154  for(int x = 0; x < 4; x++)
2155  {
2156  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
2157  }
2158  }
2159  else
2160  {
2161  for(int x = 0; x < 4; x++)
2162  {
2164  }
2165  }
2166  }
2167  if(TrainMode == Timetable)
2168  {
2170  }
2171  else
2172  {
2174  }
2176 
2177  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
2178  if(LeadElement > -1)
2179  {
2180  if(Straddle == MidLag)
2181  // just about to move half onto the new lead element
2182  {
2184  // pick up new background bitmap [0]
2186  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
2187  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
2188  // check if own ID for entry at continuation, else crashes into itself!
2189  {
2190  // OK if crossing on a bridge
2191  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
2192  if(OtherTrainEntryPos == -1)
2193  {
2194  throw Exception("Error - OtherTrainEntryPos not set");
2195  }
2196  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
2197  // LeadEntryPos for rear end crashes
2198  (LeadExitPos == OtherTrainEntryPos))
2199  // LeadExitPos for head-on crashes
2200  {
2202  Crashed = true; // only set if Straddle = MidLag
2203  CallingOnFlag = false;
2204  // in case was set, need to disable call on if call on button had been pressed
2205  }
2206  }
2207  else if(MidElement > -1) // will be -1 for continuation entries
2208  {
2209  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
2210  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
2211  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
2212  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
2213  int OtherTrainID = -1;
2214  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
2215  {
2216  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
2217  {
2218  TrainCrashedInto = OtherTrainID;
2219  Crashed = true; // only set if Straddle = MidLag
2220  CallingOnFlag = false;
2221  // in case was set, need to disable call on if call on button had been pressed
2222  }
2223  }
2224  }
2225  }
2226  else
2227  {
2229  // pick up new background bitmap [0]
2231  }
2232  PlotElement[0] = LeadElement;
2234  PlotTrainGraphic(12, 0, Display);
2235  }
2236  if(MidElement > -1)
2237  {
2238  PlotElement[2] = MidElement;
2240  PlotTrainGraphic(1, 2, Display);
2241  }
2242  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2243  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2244  if(Straddle == MidLag)
2245  {
2246  if(MidElement > -1)
2247  {
2248  PlotElement[1] = MidElement;
2250  PlotTrainGraphic(2, 1, Display);
2251  }
2252  if(LagElement > -1)
2253  {
2254  PlotElement[3] = LagElement;
2256  PlotTrainGraphic(3, 3, Display);
2257  }
2258  }
2259  else // Straddle == LeadMidLag
2260  {
2261  if(LeadElement > -1)
2262  {
2263  PlotElement[1] = LeadElement;
2265  PlotTrainGraphic(4, 1, Display);
2266  }
2267  if(MidElement > -1)
2268  {
2269  PlotElement[3] = MidElement;
2271  PlotTrainGraphic(5, 3, Display);
2272  }
2273  }
2274  if(Crashed)
2275  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2276  {
2281  // in case was set, need to disable call on if call on button had been pressed
2288  Straddle = LeadMidLag;
2289  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2290  Display->Update();
2291  // resurrected when Update() dropped from PlotOutput etc
2292  Utilities->CallLogPop(660);
2293  return;
2294  }
2295  // deal here with station stops & pass times after all replotting done but before Straddle changed
2296  if(TrainMode == Timetable)
2297  {
2298  if(Straddle == LeadMidLag)
2299  {
2300  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2301  {
2302  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2303  // to point to the location arrival entry - before a change of direction
2304  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2305  bool StopRequired = false;
2306  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired); //excludes continuations
2307  if(TTVPos > -1) // -1 if can't find it or if name is ""
2308  {
2309  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2310  // or a station where next element contains a train or a stop signal, if so
2311  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2312  // to test the actual track the train is on since it can't be a platform
2313  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2314  TTrackElement NextTrackElement; // default for now
2315  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2316  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2317  bool TrainAtStopLinkPos3 = (LeadTrackElement.StationEntryStopLinkPos3 == LeadEntryPos);
2318  bool TrainAtStopLinkPos4 = (LeadTrackElement.StationEntryStopLinkPos4 == LeadEntryPos);
2319  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2320  int NextElementEntryPos = -1;
2321  int NextElementExitPos = -1;
2322  bool TrainOnNextElement = false;
2323  bool StopSignalAtNextElement = false;
2324  if(ForwardConnection)
2325  // if no forward connection can't derive anything from it without errors
2326  {
2327  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2328  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2329  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2330  // this is only for signals so no need to worry about points ambiguity
2331  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2332  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2333  }
2334  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || TrainAtStopLinkPos3 || TrainAtStopLinkPos4 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2335  {
2336  if(TTVPos > 0)
2337  {
2339  ActionVectorEntryPtr += TTVPos;
2340  }
2341  if(StopRequired)
2342  {
2343  StoppedAtLocation = true;
2344  StoppedAtSignal = false;
2345  // may have been set earlier at line 925 so need to reset as
2346  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2347  // in zoom out mode
2348  if(!TrainFailed)
2349  {
2351  // pale green
2352  }
2354  ActualArrivalTime = TrainController->TTClockTime; //added at v2.13.0
2356  {
2357  TimeTimeLocArrived = true;
2358  // used in case of later signaller control, when need to know
2359  // whether had arrived or not, to avoid sending the arrival
2360  // message twice, see TInterface::TimetableControl1Click
2361  }
2362  }
2363  else
2364  {
2366  }
2368  {
2370  }
2371  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2373  }
2374  }
2375  }
2376  }
2377  }
2378  if(Straddle == MidLag)
2379  {
2380  Straddle = LeadMidLag;
2381  FirstHalfMove = false;
2382  }
2383  else if(Straddle == LeadMidLag)
2384  {
2385  Straddle = LeadMid;
2386  FirstHalfMove = true;
2387  }
2388  else if(Straddle == LeadMid)
2389  {
2390  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2391  }
2392  if(TrainFailurePending) // ok, moving but PlotElements set above
2393  {
2394  TrainHasFailed(7);
2395  }
2396  Display->Update();
2397  // need to keep this since Update() not called for PlotSmallOutput as too slow
2398  Utilities->CallLogPop(661);
2399 }
2400 
2401 // ----------------------------------------------------------------------------
2402 
2403 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2404 {
2405  switch(CodeChar)
2406  {
2407  case '0':
2408  return(RailGraphics->Code0);
2409 
2410  case '1':
2411  return(RailGraphics->Code1);
2412 
2413  case '2':
2414  return(RailGraphics->Code2);
2415 
2416  case '3':
2417  return(RailGraphics->Code3);
2418 
2419  case '4':
2420  return(RailGraphics->Code4);
2421 
2422  case '5':
2423  return(RailGraphics->Code5);
2424 
2425  case '6':
2426  return(RailGraphics->Code6);
2427 
2428  case '7':
2429  return(RailGraphics->Code7);
2430 
2431  case '8':
2432  return(RailGraphics->Code8);
2433 
2434  case '9':
2435  return(RailGraphics->Code9);
2436 
2437  case 'A':
2438  return(RailGraphics->CodeA);
2439 
2440  case 'B':
2441  return(RailGraphics->CodeB);
2442 
2443  case 'C':
2444  return(RailGraphics->CodeC);
2445 
2446  case 'D':
2447  return(RailGraphics->CodeD);
2448 
2449  case 'E':
2450  return(RailGraphics->CodeE);
2451 
2452  case 'F':
2453  return(RailGraphics->CodeF);
2454 
2455  case 'G':
2456  return(RailGraphics->CodeG);
2457 
2458  case 'H':
2459  return(RailGraphics->CodeH);
2460 
2461  case 'I':
2462  return(RailGraphics->CodeI);
2463 
2464  case 'J':
2465  return(RailGraphics->CodeJ);
2466 
2467  case 'K':
2468  return(RailGraphics->CodeK);
2469 
2470  case 'L':
2471  return(RailGraphics->CodeL);
2472 
2473  case 'M':
2474  return(RailGraphics->CodeM);
2475 
2476  case 'N':
2477  return(RailGraphics->CodeN);
2478 
2479  case 'O':
2480  return(RailGraphics->CodeO);
2481 
2482  case 'P':
2483  return(RailGraphics->CodeP);
2484 
2485  case 'Q':
2486  return(RailGraphics->CodeQ);
2487 
2488  case 'R':
2489  return(RailGraphics->CodeR);
2490 
2491  case 'S':
2492  return(RailGraphics->CodeS);
2493 
2494  case 'T':
2495  return(RailGraphics->CodeT);
2496 
2497  case 'U':
2498  return(RailGraphics->CodeU);
2499 
2500  case 'V':
2501  return(RailGraphics->CodeV);
2502 
2503  case 'W':
2504  return(RailGraphics->CodeW);
2505 
2506  case 'X':
2507  return(RailGraphics->CodeX);
2508 
2509  case 'Y':
2510  return(RailGraphics->CodeY);
2511 
2512  case 'Z':
2513  return(RailGraphics->CodeZ);
2514 
2515  case 'a':
2516  return(RailGraphics->Code_a);
2517 
2518  case 'b':
2519  return(RailGraphics->Code_b);
2520 
2521  case 'c':
2522  return(RailGraphics->Code_c);
2523 
2524  case 'd':
2525  return(RailGraphics->Code_d);
2526 
2527  case 'e':
2528  return(RailGraphics->Code_e);
2529 
2530  case 'f':
2531  return(RailGraphics->Code_f);
2532 
2533  case 'g':
2534  return(RailGraphics->Code_g);
2535 
2536  case 'h':
2537  return(RailGraphics->Code_h);
2538 
2539  case 'i':
2540  return(RailGraphics->Code_i);
2541 
2542  case 'j':
2543  return(RailGraphics->Code_j);
2544 
2545  case 'k':
2546  return(RailGraphics->Code_k);
2547 
2548  case 'l':
2549  return(RailGraphics->Code_l);
2550 
2551  case 'm':
2552  return(RailGraphics->Code_m);
2553 
2554  case 'n':
2555  return(RailGraphics->Code_n);
2556 
2557  case 'o':
2558  return(RailGraphics->Code_o);
2559 
2560  case 'p':
2561  return(RailGraphics->Code_p);
2562 
2563  case 'q':
2564  return(RailGraphics->Code_q);
2565 
2566  case 'r':
2567  return(RailGraphics->Code_r);
2568 
2569  case 's':
2570  return(RailGraphics->Code_s);
2571 
2572  case 't':
2573  return(RailGraphics->Code_t);
2574 
2575  case 'u':
2576  return(RailGraphics->Code_u);
2577 
2578  case 'v':
2579  return(RailGraphics->Code_v);
2580 
2581  case 'w':
2582  return(RailGraphics->Code_w);
2583 
2584  case 'x':
2585  return(RailGraphics->Code_x);
2586 
2587  case 'y':
2588  return(RailGraphics->Code_y);
2589 
2590  case 'z':
2591  return(RailGraphics->Code_z);
2592 
2593  default:
2594  return(RailGraphics->TempHeadCode);
2595  }
2596 }
2597 
2598 // ----------------------------------------------------------------------------
2599 
2600 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2601 {
2602  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2603  if(Code.Length() != 4)
2604  {
2605  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2606  }
2607  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2608  {
2609  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2610  }
2611  if(BackgroundColour != clB5G5R5)
2612  // i.e. not the basic graphic colour as loaded from resource file
2613  {
2614  for(int x = 0; x < 4; x++)
2615  {
2617  }
2618  }
2619  Utilities->CallLogPop(1484);
2620 }
2621 
2622 // ----------------------------------------------------------------------------
2623 
2624 void TTrain::GetLeadElement(int Caller)
2625 // assumes Mid & Lag already set, sets LeadElement,
2626 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2627 {
2628  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2629  DerailPending = false;
2633  {
2634  // attr 0=straight, - links 0 & 1 (0 = lead)
2635  // attr 1=diverging, - links 2 & 3 (2 = lead)
2636  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2637  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2638 
2639  // if enter at lead, exit at whatever attr set at
2640  // if enter at lag, exit at lead, but set derail wrt attribute
2641  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2642  {
2643  LeadExitPos = 1;
2644  }
2645 
2646  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2647  // best to be on safe side
2648  else if(LeadEntryPos == 0)
2649  {
2650  LeadEntryPos = 2;
2651  LeadExitPos = 3;
2652  }
2653  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2654  {
2655  LeadEntryPos = 0;
2656  LeadExitPos = 1;
2657  }
2658  else if(LeadEntryPos == 2)
2659  {
2660  LeadExitPos = 3;
2661  }
2662  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2663  {
2664  LeadExitPos = 0;
2665  }
2666  else if(LeadEntryPos == 1)
2667  {
2668  LeadExitPos = 0;
2669  DerailPending = true;
2670  }
2671  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2672  {
2673  LeadExitPos = 0;
2674  DerailPending = true;
2675  }
2676  else if(LeadEntryPos == 3)
2677  {
2678  LeadExitPos = 0;
2679  }
2680  }
2681  else if(LeadEntryPos == 0)
2682  {
2683  LeadExitPos = 1;
2684  }
2685  else if(LeadEntryPos == 1)
2686  {
2687  LeadExitPos = 0;
2688  }
2689  else if(LeadEntryPos == 2)
2690  {
2691  LeadExitPos = 3;
2692  }
2693  else if(LeadEntryPos == 3)
2694  {
2695  LeadExitPos = 2;
2696  }
2697  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2698 /* signal check moved to Update() function
2699  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2700  && (TrackElement.Attribute == 0))//0 = red
2701  {
2702  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2703  }
2704  else
2705  {
2706  StoppedAtSignal = false;
2707  }
2708 */
2709  Utilities->CallLogPop(662);
2710 }
2711 
2712 // ----------------------------------------------------------------------------
2713 
2714 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2715 {
2716  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2717  switch(Link)
2718  {
2719  case 1:
2720  {
2721  HOffset = 0;
2722  VOffset = 0;
2723  break;
2724  }
2725 
2726  case 2:
2727  {
2728  HOffset = 4;
2729  VOffset = 0;
2730  break;
2731  }
2732 
2733  case 3:
2734  {
2735  HOffset = 8;
2736  VOffset = 0;
2737  break;
2738  }
2739 
2740  case 4:
2741  {
2742  HOffset = 0;
2743  VOffset = 4;
2744  break;
2745  }
2746 
2747  case 6:
2748  {
2749  HOffset = 8;
2750  VOffset = 4;
2751  break;
2752  }
2753 
2754  case 7:
2755  {
2756  HOffset = 0;
2757  VOffset = 8;
2758  break;
2759  }
2760 
2761  case 8:
2762  {
2763  HOffset = 4;
2764  VOffset = 8;
2765  break;
2766  }
2767 
2768  case 9:
2769  {
2770  HOffset = 8;
2771  VOffset = 8;
2772  break;
2773  }
2774 
2775  default:
2776  {
2777  throw Exception("Error in GetOffsetValues - Link value wrong");
2778  }
2779  }
2780  Utilities->CallLogPop(674);
2781 }
2782 
2783 // ---------------------------------------------------------------------------
2784 
2785 bool TTrain::LowEntryValue(int EntryLink) const
2786 {
2787 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2788  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2789 */
2790  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2791  {
2792  return(true);
2793  }
2794  else
2795  {
2796  return(false);
2797  }
2798 }
2799 
2800 // ---------------------------------------------------------------------------
2801 
2802 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2803 {
2804  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2805  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2806  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2807  // default values
2808  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2809 
2810  TAllRoutes::TRouteType RouteType;
2811 
2812  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2813 
2814  TRect SourceRect, DestRect;
2815 
2816  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2817  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2818  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2819  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2820 
2821  TempGraphic->PixelFormat = pf8bit;
2822  TempGraphic->Width = 16;
2823  TempGraphic->Height = 16;
2824  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2825 
2826  if(TempElement.TrackType == Points)
2827  {
2828  TempGraphic->Assign(TempElement.GraphicPtr);
2829  TempGraphic->Transparent = true;
2830  TempGraphic->TransparentColor = Utilities->clTransparent;
2831  if(RouteType == TAllRoutes::AutoSigsRoute)
2832  {
2833  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2834  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2835  }
2836  else
2837  {
2838  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2839  }
2840  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2841  }
2842  else if(TempElement.TrackType == GapJump) // plot set gap
2843  {
2844  if(TempElement.SpeedTag == 88)
2845  {
2846  TempGraphic->Assign(RailGraphics->gl88set);
2847  }
2848  else if(TempElement.SpeedTag == 89)
2849  {
2850  TempGraphic->Assign(RailGraphics->gl89set);
2851  }
2852  else if(TempElement.SpeedTag == 90)
2853  {
2854  TempGraphic->Assign(RailGraphics->gl90set);
2855  }
2856  else if(TempElement.SpeedTag == 91)
2857  {
2858  TempGraphic->Assign(RailGraphics->gl91set);
2859  }
2860  else if(TempElement.SpeedTag == 92)
2861  {
2862  TempGraphic->Assign(RailGraphics->gl92set);
2863  }
2864  else if(TempElement.SpeedTag == 93)
2865  {
2866  TempGraphic->Assign(RailGraphics->bm93set);
2867  }
2868  else if(TempElement.SpeedTag == 94)
2869  {
2870  TempGraphic->Assign(RailGraphics->bm94set);
2871  }
2872  else if(TempElement.SpeedTag == 95)
2873  {
2874  TempGraphic->Assign(RailGraphics->gl95set);
2875  }
2876  TempGraphic->Transparent = true;
2877  TempGraphic->TransparentColor = Utilities->clTransparent;
2878  if(RouteType == TAllRoutes::AutoSigsRoute)
2879  {
2880  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2881  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2882  }
2883  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2884  }
2885  // new for version 0.6
2886  else if(TempElement.TrackType == SignalPost)
2887  {
2888  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2889  {
2890  for(int x = 0; x < 40; x++)
2891  {
2892  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2893  // need to stop aspect
2894  {
2895  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2896  break;
2897  }
2898  }
2899  }
2900  else // normal signal
2901  {
2902  TempGraphic->Assign(TempElement.GraphicPtr);
2903  // GraphicPtr set to normal signal in a signal track element
2904  }
2905  TempGraphic->Transparent = true;
2906  TempGraphic->TransparentColor = Utilities->clTransparent;
2907  if(RouteType == TAllRoutes::AutoSigsRoute)
2908  {
2909  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2910  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2911  }
2912  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2913  }
2914  else
2915  {
2916  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2917  // can't name points gaps or signals so 'else' OK
2918  bool FoundFlag;
2919  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2920  if(FoundFlag)
2921  {
2923  {
2924  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2925  TempGraphic->Assign(RailGraphics->bmName);
2926  TempGraphic->Transparent = true;
2927  TempGraphic->TransparentColor = Utilities->clTransparent;
2928  if(RouteType == TAllRoutes::AutoSigsRoute)
2929  {
2930  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2931  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2932  }
2933  else
2934  {
2935  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2936  }
2937  // draw track on top
2938  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2939  }
2940  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2941  {
2942  TempGraphic->Assign(TempElement.GraphicPtr);
2943  TempGraphic->Transparent = true;
2944  TempGraphic->TransparentColor = Utilities->clTransparent;
2945  // note that can't be an AutoSigsRoute
2946  // now overlay the LC central portion
2947  int BDVectorPos = -1; //not used
2948  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2949  {
2950  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2951  }
2952  else
2953  {
2954  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2955  }
2956  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2957  }
2958  else
2959  {
2960  TempGraphic->Assign(TempElement.GraphicPtr);
2961  TempGraphic->Transparent = true;
2962  TempGraphic->TransparentColor = Utilities->clTransparent;
2963  if(RouteType == TAllRoutes::AutoSigsRoute)
2964  {
2965  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2966  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2967  }
2968  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2969  }
2970  }
2971  else
2972  {
2973  TempGraphic->Assign(TempElement.GraphicPtr);
2974  TempGraphic->Transparent = true;
2975  TempGraphic->TransparentColor = Utilities->clTransparent;
2976  if(RouteType == TAllRoutes::AutoSigsRoute)
2977  {
2978  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2979  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2980  }
2981  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2982  }
2983  }
2984  delete TempGraphic;
2985  Utilities->CallLogPop(675);
2986 }
2987 
2988 // ---------------------------------------------------------------------------
2989 
2990 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2991 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2992 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2993 /*
2994 
2995  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2996  {
2997  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2998  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2999  TAllRoutes::TRouteType RouteType;
3000  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
3001  // default values
3002  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
3003  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
3004  TRect SourceRect, DestRect, ScreenSourceRect;
3005  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
3006 
3007  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
3008  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3009  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3010 
3011  //add text & user graphics if any to *GraphicPtr prior to adding the track
3012  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
3013  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
3014  int Right = Left + 8;
3015  int Bottom = Top + 8;
3016  ScreenSourceRect.init(Left, Top, Right, Bottom);
3017  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
3018  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
3019 
3020  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
3021  TempGraphic->PixelFormat = pf8bit;
3022  TempGraphic->Width = 16;
3023  TempGraphic->Height = 16;
3024 
3025  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
3026  SourceGraphic->PixelFormat = pf8bit;
3027  SourceGraphic->Width = 16;
3028  SourceGraphic->Height = 16;
3029  SourceGraphic->Transparent = true;
3030  SourceGraphic->TransparentColor = Utilities->clTransparent;
3031 
3032  if (TempElement.TrackType == Points)
3033  {
3034  TempGraphic->Assign(TempElement.GraphicPtr);
3035  TempGraphic->Transparent = true;
3036  TempGraphic->TransparentColor = Utilities->clTransparent;
3037  if (RouteType == TAllRoutes::AutoSigsRoute)
3038  {
3039  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3040  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3041  }
3042  else
3043  {
3044  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
3045  }
3046  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3047  }
3048  else if (TempElement.TrackType == GapJump) // plot set gap
3049  {
3050  if (TempElement.SpeedTag == 88)
3051  TempGraphic->Assign(RailGraphics->gl88set);
3052  else if (TempElement.SpeedTag == 89)
3053  TempGraphic->Assign(RailGraphics->gl89set);
3054  else if (TempElement.SpeedTag == 90)
3055  TempGraphic->Assign(RailGraphics->gl90set);
3056  else if (TempElement.SpeedTag == 91)
3057  TempGraphic->Assign(RailGraphics->gl91set);
3058  else if (TempElement.SpeedTag == 92)
3059  TempGraphic->Assign(RailGraphics->gl92set);
3060  else if (TempElement.SpeedTag == 93)
3061  TempGraphic->Assign(RailGraphics->bm93set);
3062  else if (TempElement.SpeedTag == 94)
3063  TempGraphic->Assign(RailGraphics->bm94set);
3064  else if (TempElement.SpeedTag == 95)
3065  TempGraphic->Assign(RailGraphics->gl95set);
3066  TempGraphic->Transparent = true;
3067  TempGraphic->TransparentColor = Utilities->clTransparent;
3068  if (RouteType == TAllRoutes::AutoSigsRoute) {
3069  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3070  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3071  }
3072  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3073  }
3074  // new for version 0.6
3075  else if (TempElement.TrackType == SignalPost)
3076  {
3077  if (TempElement.SigAspect == TTrackElement::GroundSignal)
3078  {
3079  for (int x = 0; x < 40; x++)
3080  {
3081  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
3082  // need to stop aspect
3083  {
3084  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
3085  break;
3086  }
3087  }
3088  }
3089  else // normal signal
3090  {
3091  TempGraphic->Assign(TempElement.GraphicPtr);
3092  // GraphicPtr set to normal signal in a signal track element
3093  }
3094  TempGraphic->Transparent = true;
3095  TempGraphic->TransparentColor = Utilities->clTransparent;
3096  if (RouteType == TAllRoutes::AutoSigsRoute) {
3097  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3098  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3099  }
3100  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3101  }
3102  else {
3103  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
3104  // can't name points gaps or signals so 'else' OK
3105  bool FoundFlag;
3106  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
3107  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
3108  if (FoundFlag)
3109  {
3110  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
3111  {
3112  GraphicPtr->Canvas->CopyRect(DestRect,
3113  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
3114  TempGraphic->Assign(RailGraphics->bmName);
3115  TempGraphic->Transparent = true;
3116  TempGraphic->TransparentColor = Utilities->clTransparent;
3117  if (RouteType == TAllRoutes::AutoSigsRoute)
3118  {
3119  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3120  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3121  }
3122  else
3123  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
3124  // draw track on top
3125  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3126  SourceRect);
3127  }
3128  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
3129  TempGraphic->Assign(TempElement.GraphicPtr);
3130  TempGraphic->Transparent = true;
3131  TempGraphic->TransparentColor = Utilities->clTransparent;
3132  // note that can't be an AutoSigsRoute
3133  // now overlay the LC central portion
3134  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
3135  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3136  SourceRect);
3137  }
3138  else {
3139  TempGraphic->Assign(TempElement.GraphicPtr);
3140  TempGraphic->Transparent = true;
3141  TempGraphic->TransparentColor = Utilities->clTransparent;
3142  if (RouteType == TAllRoutes::AutoSigsRoute) {
3143  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3144  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3145  }
3146  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
3147  SourceRect);
3148  }
3149  }
3150  else {
3151  TempGraphic->Assign(TempElement.GraphicPtr);
3152  TempGraphic->Transparent = true;
3153  TempGraphic->TransparentColor = Utilities->clTransparent;
3154  if (RouteType == TAllRoutes::AutoSigsRoute) {
3155  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
3156  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
3157  }
3158  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
3159  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
3160  }
3161  }
3162  delete TempGraphic;
3163  delete SourceGraphic;
3164  Utilities->CallLogPop();
3165  }
3166 */
3167 // ---------------------------------------------------------------------------
3168 
3169 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
3170 {
3171  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
3172  if(PlotElement[ArrayNumber] == -1)
3173  {
3174  Utilities->CallLogPop(676);
3175  return; // not plotted yet
3176  }
3177  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
3178  // set before plot so gap flashing stops first
3179  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3180  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
3181  // Only need to set ID for leading element, stays set until train finally leaves the element
3182  Plotted = true;
3183  Utilities->CallLogPop(677);
3184 }
3185 
3186 // ---------------------------------------------------------------------------
3187 
3188 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
3189 {
3190  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
3191  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
3192 }
3193 
3194 // ---------------------------------------------------------------------------
3195 
3196 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
3197 {
3198  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
3199  HeadCode);
3200  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
3201  {
3202  Utilities->CallLogPop(678);
3203  return(true);
3204  }
3205  else
3206  {
3207  Utilities->CallLogPop(679);
3208  return(false);
3209  }
3210 }
3211 
3212 // ---------------------------------------------------------------------------
3213 
3214 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
3215 {
3216  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
3217  "," + HeadCode);
3218  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
3219  {
3220  Utilities->CallLogPop(680);
3221  return(true);
3222  }
3223  else
3224  {
3225  Utilities->CallLogPop(681);
3226  return(false);
3227  }
3228 }
3229 
3230 // ---------------------------------------------------------------------------
3231 
3232 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
3233 // test whether this train on a bridge on trackpos 0 & 1
3234 {
3235  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
3236  HeadCode);
3237  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
3238  {
3239  Utilities->CallLogPop(682);
3240  return(false);
3241  }
3242  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3244  {
3246  {
3247  throw Exception("Error, same train on two different bridge tracks");
3248  }
3249  else
3250  {
3251  Utilities->CallLogPop(684);
3252  return(true);
3253  }
3254  }
3255  else
3256  {
3257  Utilities->CallLogPop(685);
3258  return(false);
3259  }
3260 }
3261 
3262 // ---------------------------------------------------------------------------
3263 
3264 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3265 // test whether this train on a bridge on trackpos 2 & 3
3266 {
3267  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3268  HeadCode);
3269  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3270  {
3271  Utilities->CallLogPop(686);
3272  return(false);
3273  }
3274  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3276  {
3277  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3278  Utilities->CallLogPop(687);
3279  return(true);
3280  }
3281  else
3282  {
3283  Utilities->CallLogPop(688);
3284  return(false);
3285  }
3286 }
3287 
3288 // ---------------------------------------------------------------------------
3289 
3290 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3291 {
3292  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3293  AnsiString(EntryPos) + "," + HeadCode);
3294  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3295 
3296  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3297  if(Track->GapFlashFlag)
3298  {
3300  {
3303  Track->GapFlashFlag = false;
3304  }
3305  }
3306  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3307  {
3308  if(EntryPos == -1)
3309  {
3310  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3311  }
3312  if(EntryPos < 2)
3313  {
3315  }
3316  else
3317  {
3319  }
3320  }
3321  Utilities->CallLogPop(690);
3322 }
3323 
3324 // ---------------------------------------------------------------------------
3325 
3326 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3327 {
3328  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3329  AnsiString(EntryPos) + "," + HeadCode);
3330  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3331  {
3332  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3333  }
3334  else
3335  {
3336  if(EntryPos == -1)
3337  {
3338  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3339  }
3340  if(EntryPos < 2)
3341  {
3342  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 = -1;
3343  }
3344  else
3345  {
3346  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 = -1;
3347  }
3348  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
3349  // i.e. other train on track 2&3
3350  {
3351  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
3352  }
3353  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
3354  // i.e. other train on track 1&2
3355  {
3356  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
3357  }
3358  else
3359  {
3360  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3361  }
3362  }
3363  Utilities->CallLogPop(691);
3364 }
3365 
3366 // ---------------------------------------------------------------------------
3367 
3368 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3369 {
3370  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3371  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3372  int LockedVectorNumber;
3373 
3374  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3375  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3376  {
3377  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3378  Utilities->CallLogPop(692);
3379  return;
3380  }
3381  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3382  // i.e other track is in a marked route
3383  // LinkPos doesn't have to be the entry position for the above check
3384  {
3385  TRect SourceRect, DestRect;
3386  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3387  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3388  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3389  // identify the route element for the other track
3390  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3391  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3392  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3393  int FirstELink, SecondELink = -1;
3394  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3395  // must be at least one
3396  if(RoutePair2.first > -1)
3397  {
3398  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3399  }
3400  TPrefDirElement RouteElement;
3401  // Graphics::TBitmap *RouteGraphic;
3402  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3403  // i.e. other track is in RoutePair2
3404  {
3405  if(SecondELink == -1)
3406  {
3407  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3408  }
3409  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3410  // error if both have same Link number
3411  {
3412  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3413  }
3414  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3415  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3416  }
3417  else // other track is in RoutePair1
3418  {
3419  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3420  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3421  }
3422  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3423  DestGraphic->PixelFormat = pf8bit;
3424  DestGraphic->Width = 8;
3425  DestGraphic->Height = 8;
3426  DestGraphic->Transparent = true;
3427  // has to be transparent or will overwrite the track that the train has just left
3428  DestGraphic->TransparentColor = Utilities->clTransparent;
3429  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3430  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3431  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3432  // plot locked route marker for other route if appropriate
3433  TPrefDirElement PrefDirElement; // holder for next call, unused
3434  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3435  if(StraddleValue == LeadMidLag)
3436  {
3438  PrefDirElement, LockedVectorNumber))
3439  {
3440  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3441  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3442  }
3443  }
3444  delete DestGraphic;
3445  }
3446  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3447  // also can only be a bridge or trains either have already or soon will crash
3448  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3449  {
3450  Utilities->CallLogPop(695);
3451  return;
3452  }
3453  if(ElementEntryPos > 1) // other train is on track 01
3454  {
3456  {
3458  }
3459  }
3460  else // other train is on track 23
3461  {
3463  {
3465  }
3466  }
3467  Utilities->CallLogPop(696);
3468 }
3469 
3470 // ---------------------------------------------------------------------------
3471 
3472 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3473 {
3474  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3475  AnsiString(EntryPos) + "," + HeadCode);
3476  int RouteNumber;
3477  bool WrongRoute = false;
3478  TPrefDirElement RouteElement;
3480  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3481 
3482  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3483  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3484  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3485  {
3486  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3487  {
3488  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3489  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3490  {
3491  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3492  {
3493  // don't call for stub end routes
3495  }
3496  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3497  Utilities->CallLogPop(697);
3498  return;
3499  }
3500  }
3501  // also need to check for a route on a crossing diagonal
3502  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3503  int LinkNumber = TrackElement.Link[EntryPos];
3504  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3505  {
3506  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3507  {
3508  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3509  bool LogActionErrorCalled = false;
3510  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3511  if(LinkNumber == 1)
3512  {
3513  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3514  {
3515  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3516  {
3517  // don't call for stub end routes
3519  LogActionErrorCalled = true;
3520  }
3521  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3522  }
3523  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3524  // not else in case have different routes on each diagonal, though shouldn't be possible
3525  {
3526  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3527  {
3528  // don't call for stub end routes
3530  }
3531  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3532  }
3533  }
3534 
3535  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3536  else if(LinkNumber == 3)
3537  {
3538  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3539  {
3540  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3541  {
3542  // don't call for stub end routes
3544  LogActionErrorCalled = true;
3545  }
3546  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3547  }
3548  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3549  // not else in case have different routes on each diagonal, though shouldn't be possible
3550  {
3551  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3552  {
3553  // don't call for stub end routes
3555  }
3556  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3557  }
3558  }
3559 
3560  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3561  else if(LinkNumber == 7)
3562  {
3563  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3564  {
3565  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3566  {
3567  // don't call for stub end routes
3569  LogActionErrorCalled = true;
3570  }
3571  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3572  }
3573  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3574  // not else in case have different routes on each diagonal, though shouldn't be possible
3575  {
3576  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3577  {
3578  // don't call for stub end routes
3580  }
3581  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3582  }
3583  }
3584 
3585  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3586  else if(LinkNumber == 9)
3587  {
3588  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3589  {
3590  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3591  {
3592  // don't call for stub end routes
3594  LogActionErrorCalled = true;
3595  }
3596  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3597  }
3598  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3599  // not else in case have different routes on each diagonal, though shouldn't be possible
3600  {
3601  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3602  {
3603  // don't call for stub end routes
3605  }
3606  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3607  }
3608  }
3609  }
3610  }
3611  Utilities->CallLogPop(698);
3612  return; // no route on other track or no other track
3613  }
3614  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3615  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3616  {
3617  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3618  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3619  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3620  {
3621  if(RouteElement.GetELinkPos() == EntryPos)
3622  {
3623  Utilities->CallLogPop(699);
3624  return; // right direction
3625  }
3626  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3627  {
3628  Utilities->CallLogPop(700);
3629  return; // right direction (points)
3630  }
3631  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3632  {
3633  Utilities->CallLogPop(701);
3634  return; // right direction (points)
3635  }
3636  else if(RouteElement.GetXLinkPos() == EntryPos)
3637  {
3638  WrongRoute = true;
3639  break; // wrong direction
3640  }
3641  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3642  {
3643  WrongRoute = true;
3644  break; // wrong direction
3645  }
3646  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3647  {
3648  WrongRoute = true;
3649  break; // wrong direction
3650  }
3651  }
3652  }
3653  if(!WrongRoute)
3654  {
3655  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3656  }
3657  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3658  {
3659  // don't call for stub end routes
3661  }
3662  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3663  Utilities->CallLogPop(703);
3664 }
3665 
3666 // ---------------------------------------------------------------------------
3667 
3668 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3669 {
3670  if(BackgroundColour == NewBackgroundColour)
3671  {
3672  return; // don't replot if already correct
3673 
3674  }
3675  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3676  bool ColourError = false, ColourError2 = false;
3677 
3678  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3679  if(ColourError)
3680  {
3681  ColourError2 = true;
3682  }
3683  for(int x = 0; x < 4; x++)
3684  {
3685  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3686  if(ColourError)
3687  {
3688  ColourError2 = true;
3689  }
3690  }
3691  if(ColourError2)
3692  {
3694  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3695  }
3696  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3697  // of motion
3698  for(int x = 0; x < 4; x++)
3699  {
3700  PlotTrainGraphic(6, x, Disp);
3701  }
3702  BackgroundColour = NewBackgroundColour;
3703  Display->Update();
3704  // need to keep this since Update() not called for PlotSmallOutput as too slow
3705  Utilities->CallLogPop(704);
3706 }
3707 
3708 // ---------------------------------------------------------------------------
3709 
3710 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3711 /*
3712 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3713 
3714 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3715 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3716 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3717 full-element moves.
3718 
3719 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3720 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3721 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3722 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3723 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3724 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3725 In this case set the brake rate to maximum to stop as soon as possible.
3726 
3727 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3728 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3729 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3730 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3731 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3732 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3733 
3734 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3735 first to see whether buffers or continuation) in turn is examined: first the length of the
3736 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3737 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3738 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3739 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3740 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3741 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3742 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3743 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3744 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3745 siding then again emeregency braking may be necessary and a crash may result.
3746 
3747 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3748 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3749 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3750 buffer, then the train accelerates for half the element and brakes for the other half.
3751 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3752 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3753 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3754 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3755 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3756 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3757 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3758 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3759 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3760 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3761 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3762 
3763 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3764 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3765 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3766 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3767 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3768 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3769 MaxBrakeRate.
3770 
3771 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3772 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3773 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3774 
3775 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3776 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3777 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3778 
3779 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3780 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3781 when Straddle == LeadMidLag
3782 */
3783 {
3784  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3785  AnsiString(EntryPos) + "," + HeadCode);
3786  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3787  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3788  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3789  TrainInFrontInSignallerModeFlag = false;
3790  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3791  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3792  bool SignallerStopRequired = false;
3793 
3795  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3796 
3797  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3798 
3799  OneLengthAccelDecel = false;
3800  BrakeRate = 0;
3801  if(PowerAtRail < 1)
3802  {
3803  BrakeRate = CoastingBrakeRate; //brings train to a stop in 13km in 15min from starting speed of 100km/h (from research)
3804  }
3805 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3806  if(CurrentTrackVectorPosition > -1)
3807  {
3808  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3809  {
3810  if((EntryPos == 0) || (EntryPos == 2))
3811  {
3812  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3813  {
3814  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3815  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3816  }
3817  else
3818  {
3819  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3820  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3821  }
3822  }
3823  else if(EntryPos == 1)
3824  {
3825  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3826  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3827  }
3828  else // == 3
3829  {
3830  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3831  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3832  }
3833  }
3834  else
3835  {
3836  if(EntryPos > 1)
3837  {
3838  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3839  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3840  }
3841  else
3842  {
3843  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3844  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3845  }
3846  }
3847  EntryHalfLength = CurrentElementHalfLength;
3848  FrontElementLength = 2 * CurrentElementHalfLength;
3849  }
3850  else
3851  {
3852  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3853  }
3854  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3855  {
3856  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3857  }
3858  // check if zero entry speed with another train directly in front & if so remain stopped
3859  if((EntryPos > -1) && Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3860  {
3861  EntrySpeed = 0;
3862  ExitSpeedHalf = 0;
3863  ExitSpeedFull = 0;
3864  MaxExitSpeed = 0;
3865  BrakeRate = 0;
3866  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3867  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3868  StoppedForTrainInFront = true;
3869  TrainInFront = true;
3870  Utilities->CallLogPop(705);
3871  return;
3872  }
3873  // new at v2.4.0 - check for stopped and zero power
3874  if((EntrySpeed < 1) && PowerAtRail < 1)
3875  {
3876  EntrySpeed = 0;
3877  ExitSpeedHalf = 0;
3878  ExitSpeedFull = 0;
3879  MaxExitSpeed = 0;
3880  BrakeRate = 0;
3881  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3882  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3883  StoppedWithoutPower = true;
3884  Utilities->CallLogPop(2125);
3885  return;
3886  }
3887 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3888  if(BeingCalledOn)
3889  {
3890  LimitingSpeed = CallOnMaxSpeed;
3891  }
3892  else
3893  {
3894  LimitingSpeed = MaximumSpeedLimit;
3895  }
3896  if(LimitingSpeed > FrontElementSpeedLimit)
3897  {
3898  LimitingSpeed = FrontElementSpeedLimit;
3899  }
3900  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3901  {
3902  LimitingSpeed = MaxRunningSpeed;
3903  }
3904  FrontElementMaxSpeed = LimitingSpeed;
3905 
3906 /*
3907  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3908  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3909  (2) V/3.6 = U/3.6 - FT;
3910  (3) S = UT/3.6 - 0.5FT^2
3911  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3912 
3913  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3914  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3915  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3916  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3917  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3918 
3919  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3920  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3921  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3922 */
3923 
3924 // check if running past a red signal without permission
3925  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) &&
3926  (Track->TrackElementAt(353, CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal &&
3927  !Track->TrackElementAt(1553, CurrentTrackVectorPosition).CallingOnSet)
3928  { //CallingOnSet added at v2.14.0
3929  SPADFlag = true; // user has to intervene to reset & restart after spad
3930  }
3931  if(!SPADFlag)
3932  {
3933  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3934  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3935  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3936  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3937  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3938  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3939 
3940  double ExitSpeedAtMaxBraking;
3941  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3942  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3944  {
3945  ExitSpeedAtMaxBraking = 0;
3946  }
3947  else
3948  {
3949  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3950  }
3951  double SpeedToUse;
3952  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3953  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3954  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3955  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3956  {
3957  SpeedToUse = ExitSpeedAtMaxBraking;
3958  }
3959  else
3960  {
3961  SpeedToUse = LimitingSpeed;
3962  }
3963  if(ExitSpeedFull > SpeedToUse)
3964  {
3965  ExitSpeedFull = SpeedToUse;
3966  }
3967  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3968  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3969 
3970  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3971  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3972  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3973 
3974  do
3975  {
3976  RedSignalFlag = false;
3977  BuffersFlag = false;
3978  StationFlag = false;
3979  BuffersOrContinuationNowFlag = false;
3980  ContinuationNextFlag = false;
3981  // have to reset this after the above test
3982  // add current element length to CumulativeLength
3983  CumulativeLength += (2 * CurrentElementHalfLength);
3984  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3985  {
3986  SignallerStopRequired = true;
3987  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3988  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3989  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3990  if(SignallerStopBrakeRate < TempBR)
3991  {
3992  SignallerStopBrakeRate = TempBR;
3993  }
3994  }
3995  // first check for stops within the length of the current element, where don't want any more checks & don't want
3996  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3997  // during the last loop when the NextTrackVectorPosition was the signal.
3998 
3999  // check if current element is a buffer
4000  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
4001  {
4002  // no need to add in the length of this element to CumulativeLength as already included
4003  BuffersFlag = true;
4004  }
4005  // check if current element is a station stop
4006  if(TrainMode == Timetable)
4007  {
4008  bool StopRequired = false;
4009  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
4010  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
4011  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos) ||
4012  (Track->TrackElementAt(1642, CurrentTrackVectorPosition).StationEntryStopLinkPos3 == EntryPos) ||
4013  (Track->TrackElementAt(1643, CurrentTrackVectorPosition).StationEntryStopLinkPos4 == EntryPos)))
4014  {
4015  // no need to add in the length of element to CumulativeLength
4016  if(StopRequired)
4017  {
4018  StationFlag = true;
4019  }
4020  }
4021  }
4022  else
4023  {
4024  StationFlag = false;
4025  }
4026  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
4027  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
4028  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4029  {
4030  BuffersOrContinuationNowFlag = true;
4031  }
4032  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
4033  {
4034  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
4035  {
4036  if((EntryPos == 0) || (EntryPos == 2))
4037  {
4038  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
4039  {
4040  ExitPos = 1;
4041  }
4042  else
4043  {
4044  ExitPos = 3;
4045  }
4046  }
4047  else
4048  {
4049  ExitPos = 0;
4050  }
4051  }
4052  else
4053  {
4054  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4055  }
4056  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
4057  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4058  if(NextTrackVectorPosition > -1)
4059  {
4060  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
4061  // this test & section added at v0.6
4062  {
4063  if((NextEntryPos == 0) || (NextEntryPos == 2))
4064  {
4065  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
4066  {
4067  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
4068  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
4069  }
4070  else
4071  {
4072  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
4073  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
4074  }
4075  }
4076  else if(NextEntryPos == 1)
4077  {
4078  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
4079  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
4080  }
4081  else // == 3
4082  {
4083  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
4084  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
4085  }
4086  }
4087  else
4088  {
4089  if(NextEntryPos > 1)
4090  {
4091  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
4092  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
4093  }
4094  else
4095  {
4096  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
4097  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
4098  }
4099  }
4100  }
4101  else
4102  {
4103  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
4104  }
4105  // now check for stops, first cover those where don't want to add in length of next element
4106  // check if next element is a red signal - Attr 0,
4107  // note that this doesn't apply to trains stopped at a red signal since the signal position is
4108  // CurrentTrackVectorPosition not NextTrackVectorPosition
4109  bool StopRequired;
4110  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
4111  {
4112  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
4113  {
4114  // no need to add in the length of element to CumulativeLength
4115  RedSignalFlag = true;
4116  }
4117  // next element is a red signal
4118  }
4119  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
4120  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
4121  // at least one platform element free
4123  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition, NextEntryPos, TrainID))
4124  {
4125  // no need to add in the length of element to CumulativeLength
4126  if(StopRequired)
4127  {
4128  StationFlag = true;
4129  }
4130  }
4131  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
4132  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
4133  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
4134  {
4135  // no need to add in the length of element to CumulativeLength
4136  TrainInFrontInSignallerModeFlag = true;
4137  }
4138  // check if next element is a buffer, but if StepForwardFlag true then need to stop before reach the buffers
4139  else if((Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers) && !StepForwardFlag)
4140  {
4141  // need to add in the length of that element to CumulativeLength
4142  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
4143  BuffersFlag = true;
4144  }
4145  // check if next element is a station stop
4147  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
4148  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
4149  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos) || (Track->TrackElementAt(1644,
4150  NextTrackVectorPosition).StationEntryStopLinkPos3 == EntryPos) || (Track->TrackElementAt(1645,
4151  NextTrackVectorPosition).StationEntryStopLinkPos4 == EntryPos)))
4152  { // need to add in the length of that element to CumulativeLength if a stop required
4153  if(StopRequired)
4154  {
4155  StationFlag = true;
4156  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
4157  }
4158  }
4159  }
4160  //now can decide whether need to stop over CumulativeLength
4161  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
4162  {
4163  // have to come to a stop over CumulativeLength
4164  if(CumulativeLength == FrontElementLength)
4165  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
4166  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
4167  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
4168  // and if less than EntrySpeed then skip this section (don't need any acceleration)
4169  // if not calc speed at halfway point & if less than above set half speed to this value;
4170  // use constant acceleration in calculating half time point
4171  {
4172  MaxExitSpeed = 0;
4173  double MaxHalfSpeed;
4174  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
4175  // have to halve the element length, & can't be zero or negative so no need to test
4176  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4177  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
4178  {
4179  MaxHalfSpeed = FrontElementMaxSpeed;
4180  }
4181  else
4182  {
4183  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
4184  }
4185  if(MaxHalfSpeed > (2 * EntrySpeed) && (PowerAtRail > 1)) //PowerAtRail condition added at v2.18.0
4186  // use 2x to prevent kangarooing at last element when had
4187  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
4188  {
4189  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4190  0.333334);
4191  bool HalfSpeedLimited = false;
4192  if(MaxHalfSpeed < ExitSpeedHalf)
4193  {
4194  ExitSpeedHalf = MaxHalfSpeed;
4195  HalfSpeedLimited = true;
4196  }
4197  if(PowerAtRail > 1)
4198  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4199  {
4200  // [km/h/3.6 = m/s]
4201  ExitTimeHalf =
4202  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4203  }
4204  else
4205  {
4206  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4207  }
4208  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
4209  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
4210  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
4211  // by a long braking period
4212  ExitSpeedFull = 0;
4213  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
4214  if(TempBrakeRate > MaxBrakeRate)
4215  {
4216  TempBrakeRate = MaxBrakeRate;
4217  }
4218  // shouldn't be but leave in anyway
4219  if(TempBrakeRate > BrakeRate)
4220  {
4221  BrakeRate = TempBrakeRate;
4222  }
4223  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4224  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
4225  if(HalfSpeedLimited)
4226  // this is the change referred to above
4227  {
4228  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
4229  ExitTimeHalf = EntryTime + BrakingTime;
4230  ExitTimeFull = ExitTimeHalf + BrakingTime;
4231  }
4232  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
4233  Utilities->CallLogPop(1095);
4234  return;
4235  }
4236  }
4237  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
4238  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
4239  // calc th, tf, sh, & sf
4240  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
4241  if(TempBrakeRate > MaxBrakeRate)
4242  {
4243  TempBrakeRate = MaxBrakeRate;
4244  }
4245  if(TempBrakeRate > BrakeRate)
4246  {
4247  BrakeRate = TempBrakeRate;
4248  }
4249  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4250  if(SignallerStopRequired)
4251  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4252  {
4254  {
4256  // this prevents the brakerate from reducing for a signaller stop
4257  // regardless of other conditions that may change as progress round the loop
4258  }
4259  }
4261  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4262  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4263  {
4265  }
4266  int TempMaxExitSpeed;
4267  // calc current value & if less than MaxExitSpeed set that to this
4268  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4269  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4270  {
4271  MaxExitSpeedAtHalfBraking = 0;
4272  }
4273  else
4274  {
4275  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4276  }
4277  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4278  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4279  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4280  {
4281  TempMaxExitSpeed = FrontElementMaxSpeed;
4282  }
4283  else
4284  {
4285  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4286  }
4287  if(TempMaxExitSpeed < MaxExitSpeed)
4288  {
4289  MaxExitSpeed = TempMaxExitSpeed;
4290  }
4291  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4292  // Cumulativelength, and Cumulativelength
4293 
4294  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4295  {
4296  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4297  if(ExitSpeedHalfSquared < 10)
4298  {
4299  ExitSpeedHalf = 0;
4300  }
4301  else
4302  {
4303  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4304  }
4305  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4306  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4307  if(ExitSpeedFullSquared < 10)
4308  {
4309  ExitSpeedFull = 0;
4310  }
4311  else
4312  {
4313  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4314  }
4315  if((StationFlag) && (CumulativeLength == FrontElementLength))
4316  {
4317  ExitSpeedFull = 0;
4318  // force a stop for station (not for buffers or red signal)
4319  }
4320  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4321  }
4322  // new condition at v2.4.0
4323  else if(PowerAtRail < 1)
4324  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4325  // avoid using AValue in denominator or have excessively long times
4326  {
4327  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4328  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4329  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4330 
4331  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4332  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4333  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4334  }
4335  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4336  // without the power need above condition or have hours of delay times, above added at v2.4.0
4337  {
4338  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4339  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4340  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4341  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4342  BrakeRate = 0;
4343  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4344  0.333334);
4345  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4346  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4348  // can accelerate continually over the half length
4349  {
4350  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4351  / 86400.0);
4353  // can accelerate continually over the full length
4354  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4355  {
4356  ExitTimeFull =
4357  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4358  }
4359  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4360  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4361  {
4362  // added at v0.6 as a safeguard
4363  if(MaxExitSpeed < EntrySpeed)
4364  {
4366  }
4367  // to prevent DeltaExitTimeToMaxInSecs being negative
4368  if(MaxExitSpeed < 1)
4369  {
4370  MaxExitSpeed = 1;
4371  }
4372  // to prevent divide by zero error
4373  // below as was
4375  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4376  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4377  (1.5 * AValue * AValue);
4378  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4379  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4380  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4381  }
4382  }
4383  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4384  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4385  // second halves of the element
4386  {
4387  // added at v0.6 as a safeguard
4388  if(MaxExitSpeed < EntrySpeed)
4389  {
4391  }
4392  // to prevent DeltaExitTimeToMaxInSecs being negative
4393  if(MaxExitSpeed < 1)
4394  {
4395  MaxExitSpeed = 1; // to prevent divide by zero error
4396  }
4397  // below as was
4399  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4400  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4401  (1.5 * AValue * AValue);
4402  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4403  // remaining distance to half length
4404  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4405  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4407  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4408  }
4409  }
4410  Utilities->CallLogPop(706);
4411  return;
4412  }
4413  else
4414  {
4415  if(!BuffersOrContinuationNowFlag)
4416  {
4417  if(NextSpeedLimit < LimitingSpeed)
4418  {
4419  LimitingSpeed = NextSpeedLimit;
4420  }
4421  }
4422  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4423  int TempMaxExitSpeed;
4424  // calc current value & if less than MaxExitSpeed set that to this
4425  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4426  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4427  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4428  {
4429  MaxExitSpeedAtHalfBraking = 0;
4430  }
4431  else
4432  {
4433  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4434  }
4435  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4436  {
4437  TempMaxExitSpeed = FrontElementMaxSpeed;
4438  }
4439  else
4440  {
4441  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4442  }
4443  if(TempMaxExitSpeed < MaxExitSpeed)
4444  {
4445  MaxExitSpeed = TempMaxExitSpeed;
4446  }
4447  // MaxExitSpeed is an external variable & this can reduce its value
4448  if(EntrySpeed > LimitingSpeed)
4449  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4450  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4451  {
4452  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4453  if(TempBrakeRate > MaxBrakeRate)
4454  {
4455  TempBrakeRate = MaxBrakeRate;
4456  }
4457  // shouldn't be for speedlimits since all known in advance, but include anyway
4458  if(TempBrakeRate > BrakeRate)
4459  {
4460  BrakeRate = TempBrakeRate;
4461  }
4462  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4463  }
4464  }
4465  if(!BuffersOrContinuationNowFlag)
4466  {
4467  CurrentTrackVectorPosition = NextTrackVectorPosition;
4468  EntryPos = NextEntryPos;
4469  CurrentElementHalfLength = NextElementHalfLength;
4470  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4471  {
4472  ContinuationNextFlag = true;
4473  }
4474  }
4475  }
4476  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4478  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4479  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4480  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4481  // stopping distance after it.
4482 
4483  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4484  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4485 
4486  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4487  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4488  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4489  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4490  // too late
4491 
4492  // set final braking or acc'n speed & time values
4493  if(BrakeRate > 0.01)
4494  {
4495  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4496  if(ExitSpeedHalfSquared < 10)
4497  {
4498  ExitSpeedHalf = 0;
4499  }
4500  else
4501  {
4502  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4503  }
4504  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4505  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4506  if(ExitSpeedFullSquared < 10)
4507  {
4508  ExitSpeedFull = 0;
4509  }
4510  else
4511  {
4512  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4513  }
4514  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4515  }
4516  else
4517  {
4518  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4519  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4520  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4521  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4522 
4523  BrakeRate = 0;
4524  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4525  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4526  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4528  {
4529  if(PowerAtRail > 1)
4530  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4531  {
4532  // [km/h/3.6 = m/s]
4533  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4534  / 86400.0);
4535  }
4536  else
4537  {
4538  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4539  }
4541  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4542  {
4543  if(PowerAtRail > 1)
4544  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4545  {
4546  // [km/h/3.6 = m/s]
4547  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4548  / 86400.0);
4549  }
4550  else
4551  {
4552  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4553  }
4554  }
4555  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4556  {
4557  // added at v0.6 as a safeguard
4558  if(MaxExitSpeed < EntrySpeed)
4559  {
4561  }
4562  // to prevent DeltaExitTimeToMaxInSecs being negative
4563  if(MaxExitSpeed < 1)
4564  {
4565  MaxExitSpeed = 1; // to prevent divide by zero error
4566  }
4567  // below as was
4569  double DeltaExitTimeToMaxInSecs;
4570  double DistanceToMax;
4571  if(PowerAtRail > 1) // added at v2.4.0
4572  {
4573  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4574  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4575  (1.5 * AValue * AValue);
4576  }
4577  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4578  {
4579  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4580  // these not really accurate but will be good enough
4581  DistanceToMax = EntryHalfLength;
4582  }
4583  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4584  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4585  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4586  }
4587  }
4588  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4589  {
4590  // added at v0.6 as a safeguard
4591  if(MaxExitSpeed < EntrySpeed)
4592  {
4594  }
4595  // to prevent DeltaExitTimeToMaxInSecs being negative
4596  if(MaxExitSpeed < 1)
4597  {
4598  MaxExitSpeed = 1; // to prevent divide by zero error
4599  }
4600  // below as was
4602  double DeltaExitTimeToMaxInSecs;
4603  double DistanceToMax;
4604  if(PowerAtRail > 1) // added at v2.4.0
4605  {
4606  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4607  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4608  (1.5 * AValue * AValue);
4609  }
4610  else
4611  {
4612  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4613  // these not really accurate but will be good enough
4614  DistanceToMax = EntryHalfLength / 2;
4615  }
4616  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4617  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4618  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4620  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4621  }
4622  }
4623  }
4624 
4625  else // SPADFlag set
4626  {
4628  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4629  if(ExitSpeedHalfSquared < 10)
4630  {
4631  ExitSpeedHalf = 0;
4632  }
4633  else
4634  {
4635  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4636  }
4637  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4638  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4639  if(ExitSpeedFullSquared < 10)
4640  {
4641  ExitSpeedFull = 0;
4642  }
4643  else
4644  {
4645  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4646  }
4647  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4648 
4649  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4650  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4651  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4652  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4653  // will be the stopping speed.
4654  if(ExitSpeedFull > 0)
4655  {
4656  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4657  {
4658  if((EntryPos == 0) || (EntryPos == 2))
4659  {
4660  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4661  {
4662  ExitPos = 1;
4663  }
4664  else
4665  {
4666  ExitPos = 3;
4667  }
4668  }
4669  else
4670  {
4671  ExitPos = 0;
4672  }
4673  }
4674  else
4675  {
4676  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4677  }
4678  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4679  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4680  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4681  {
4682  int NextElementLength;
4683  if(NextEntryPos > 1)
4684  {
4685  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4686  }
4687  else
4688  {
4689  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4690  }
4691  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4692  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4693  {
4694  ExitSpeedFull = 0;
4695  }
4696  }
4697  }
4698  }
4699  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4700  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4701  {
4702  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed <--NO,
4703  //change to BrakeRate = CoastingBrakeRate = 0.03 and calc times etc as normal - because of Albie Vowles' error report of 231223 where noticed
4704  //that failed train treated track lengths of > 2km as 100m so very noticeable. Keep going for exiting at continuation.
4705 
4706  //Coasting deceleration rate from paper 'Real-time train motion parameter estimation using an Unscented Kalman Filter' at
4707  //'https://www.sciencedirect.com/science/article/pii/S0968090X22002212'. In particular Fig 6 in section 4.3 shows coasting from 400sec to
4708  //1000sec corresponds to speed drop from 140km/h to 80km/h, i.e. 60km/h in 600sec, equivalent to 0.02777m/s/s deceleration, so use 0.03m/s/s.
4709 
4710  if(LeadElement > -1)
4711  {
4713  // don't stop on a continuation either entering or leaving
4714  {
4717  MaxExitSpeed = LimitingSpeed;
4718  BrakeRate = 0;
4719  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4720  // assume length is 50m for ease of calc
4721  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4722  Utilities->CallLogPop(2126);
4723  return;
4724  }
4725  }
4726  else if(MidElement > -1)
4727  {
4729  {
4732  MaxExitSpeed = LimitingSpeed;
4733  BrakeRate = 0;
4734  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4735  // assume length is 50m for ease of calc
4736  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4737  Utilities->CallLogPop(2127);
4738  return;
4739  }
4740  }
4741  else if(LagElement > -1)
4742  {
4744  {
4747  MaxExitSpeed = LimitingSpeed;
4748  BrakeRate = 0;
4749  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4750  // assume length is 50m for ease of calc
4751  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4752  Utilities->CallLogPop(2128);
4753  return;
4754  }
4755  }
4756 /* dropped at v2.18.1 in favour of CoastingBrakeRate which = 0.03m/s/s = see above explanation
4757  if(EntrySpeed > 7.5) // keep going for at least another element
4758  {
4759  ExitSpeedHalf = EntrySpeed - 2.5;
4760  ExitSpeedFull = EntrySpeed - 5;
4761  MaxExitSpeed = LimitingSpeed;
4762  BrakeRate = 0;
4763  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4764  // assume length is 50m for ease of calc
4765  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4766  Utilities->CallLogPop(2129);
4767  return;
4768  }
4769  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4770  {
4771  // will appear to have slowed at steady rate
4772  ExitSpeedHalf = 0;
4773  ExitSpeedFull = 0;
4774  MaxExitSpeed = LimitingSpeed;
4775  BrakeRate = 0;
4776  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4777  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4778  StoppedWithoutPower = true;
4779  Utilities->CallLogPop(2130);
4780  return;
4781  }
4782 */
4783  }
4784  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4785  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4786  Utilities->CallLogPop(707);
4787 }
4788 // ---------------------------------------------------------------------------
4789 /*
4790  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4791  {
4792  int NextExitPos;
4793  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4794  if(TimetableVector.empty()) return false;
4795  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4796  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4797  {
4798  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4799  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4800  {
4801  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4802  }
4803  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4804  {
4805  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4806  }
4807  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4808  NextElement = TempElement;
4809  }
4810  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4811  if(NextElement.TrackType == Buffers) return true;
4812  return false;//shouldn't reach here but include to prevent compiler return warning
4813  }
4814 */
4815 // ---------------------------------------------------------------------------
4816 
4817 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4818 /*
4819  returns the number by which the train ActionVectorEntryPtr needs
4820  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4821  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4822  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4823  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4824  or pass (false) the location.
4825 */{
4826  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4827  Stop = false;
4828  if(TimetableFinished || (Name == ""))
4829  {
4830  Utilities->CallLogPop(957);
4831  return(-1);
4832  }
4834 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4835 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4836 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4837 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4838 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4839 */
4840  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4841  {
4842  Ptr--;
4843  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4844  {
4845  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4846  {
4847  Utilities->CallLogPop(2444);
4848  return(-1);
4849  }
4850  }
4851  }
4852  // start looking from current pointer position
4853  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4854  {
4855  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4856  {
4857  break;
4858  }
4859  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4860  {
4861  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4862  {
4863  Stop = true;
4864  Utilities->CallLogPop(960);
4865  return (Ptr - ActionVectorEntryPtr);
4866  }
4867  }
4868  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4869  {
4870  Utilities->CallLogPop(1517);
4871  return (Ptr - ActionVectorEntryPtr);
4872  }
4873  }
4874  Utilities->CallLogPop(959);
4875  return(-1); // not found a valid entry
4876 }
4877 
4878 // ---------------------------------------------------------------------------
4879 
4881 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4882  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4883  Ignores the call-on signal.
4884 */{
4885  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4886  int ReturnVal = 0;
4887  int ElementCount = 0;
4888 /* dropped at v2.12.0 as takes up a great deal of time unnecessarily - substitute 1000 elements instead and return true (very unlikely to need to search this far [10km at min length])
4889  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4890  {
4891  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4892  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4893  }
4894 */
4895  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4896  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4897 
4898  while(true)
4899  {
4900  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4901  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4902  {
4903  ReturnVal = 1;
4904  break;
4905  }
4906  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4907  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4908  {
4909  ReturnVal = 2;
4910  break;
4911  }
4912  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && !Track->TrackElementAt(529,
4913  CurrentTrackVectorPosition).CallingOnSet && (LeadElement != CurrentTrackVectorPosition)) // CallingOnSet true when position lights lit,
4914  {//added LeadElement condition at v2.18.0 as train may be on the callon signal after CallOnSet false & don't want to return true for that
4915  ReturnVal = 3;
4916  break;
4917  }
4918 /* not needed at and after v2.12.0, see above
4919  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388, CurrentTrackVectorPosition).TrackType == Crossover))
4920  {
4921  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4922  // must be a loop - reached same point as examined earlier
4923  {
4924  ReturnVal = 4;
4925  break;
4926  }
4927  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4928  {
4929  ReturnVal = 4;
4930  break;
4931  }
4932  }
4933  else
4934  {
4935  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526, CurrentTrackVectorPosition).TempTrackMarker23))
4936  {
4937  ReturnVal = 4;
4938  break;
4939  }
4940  }
4941  if(EntryPos < 2)
4942  {
4943  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4944  }
4945  else
4946  {
4947  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4948  }
4949 */
4950 
4951  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4952  {
4953  if((EntryPos == 0) || (EntryPos == 2))
4954  {
4955  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4956  {
4957  ExitPos = 1;
4958  }
4959  else
4960  {
4961  ExitPos = 3;
4962  }
4963  }
4964  else
4965  {
4966  ExitPos = 0;
4967  }
4968  }
4969  else
4970  {
4971  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4972  }
4973  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4974  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4975  CurrentTrackVectorPosition = NextTrackVectorPosition;
4976  EntryPos = NextEntryPos;
4977  ElementCount++;
4978  if(ElementCount > 1000)
4979  {
4980  ReturnVal = 4;
4981  break;
4982  }
4983  }
4984  if(ReturnVal == 1)
4985  {
4986  Utilities->CallLogPop(708);
4987  return(false);
4988  }
4989  if(ReturnVal == 2)
4990  {
4991  Utilities->CallLogPop(709);
4992  return(true);
4993  }
4994  if(ReturnVal == 3)
4995  {
4996  Utilities->CallLogPop(946);
4997  return(true);
4998  }
4999  if(ReturnVal == 4)
5000  {
5001  Utilities->CallLogPop(947);
5002  return(true);
5003  }
5004  else
5005  {
5006  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
5007  }
5008 }
5009 
5010 // ---------------------------------------------------------------------------
5011 
5013 /*
5014  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
5015  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
5016  change of direction (cdt), remaining here (Frh), or under signaller control);
5017  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
5018  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
5019  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
5020  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
5021  m) not failed or stopped without power
5022  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
5023  change points outside the route or have a route conflict if another route is set.
5024 */{
5025  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
5026  {
5027  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
5028  }
5029  // some of the callingon route elements may be involved
5030  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
5031  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
5032  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
5033  int RouteStartPosition;
5034  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
5035  int PlatformPosition;
5036  // the track vector position of the first stop platfrom
5037  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
5038  // not used here
5039  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
5040  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
5041  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
5042  // must be stopped at a signal but not at a location & still in timetable (a)
5044  // no need to check for SignallerStopped as this function only called in Timetable mode
5045  {
5046  Utilities->CallLogPop(711);
5047  return(false);
5048  }
5049  while(true)
5050  {
5051  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
5052  // don't look further than 4km ahead (j)
5053  if(Distance > (4000 + LeadElementDistance))
5054  {
5055  Utilities->CallLogPop(967);
5056  return(false);
5057  }
5058  // if find another train on an element in front, before find a valid platform, return false (c)
5059  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
5060  {
5061  Utilities->CallLogPop(713);
5062  return(false);
5063  }
5064  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
5065  // be facing later on)
5066  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
5067  {
5068  // get LeadElement, if -1 return (could be exiting at continuation) (i)
5069  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
5070  if(OtherTrain.LeadElement == -1)
5071  {
5072  Utilities->CallLogPop(714);
5073  return(false);
5074  }
5075  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
5076  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
5077  {
5078  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
5079  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
5080  (OtherTrain.TrainMode == Signaller))
5081  {
5082  break;
5083  }
5084  else
5085  {
5086  Utilities->CallLogPop(955);
5087  return(false);
5088  }
5089  }
5090  else // (h)
5091  {
5092  break;
5093  }
5094  }
5095  // if reach buffers or exit continuation return false (can set route)
5096  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
5097  {
5098  Utilities->CallLogPop(716);
5099  return(false);
5100  }
5101  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
5102  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
5104  {
5105  Utilities->CallLogPop(717);
5106  return(false);
5107  }
5108  // if reach a location that isn't in timetable return false - drop this as still can't set a route
5109 /*
5110  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
5111  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
5112  {
5113  Utilities->CallLogPop(718);
5114  return false;
5115  }
5116 */
5117  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
5118  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
5119  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
5120  {
5121  if(StopRequired)
5122  {
5123  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
5124  {
5125  if(!PlatformFoundFlag)
5126  {
5127  PlatformPosition = CurrentTrackVectorPosition;
5128  }
5129  // ensure this only set once at first valid platform position - the unrestricted route will end here
5130  PlatformFoundFlag = true;
5131  }
5132  }
5133  }
5134  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
5135  // train has to be at station but that has to be before the next forward signal
5136 /*
5137  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
5138  {
5139  Utilities->CallLogPop(719);
5140  return false;
5141  }
5142 */
5143  // make sure points are followed correctly (d) & set ExitPos
5144  if(CurrentTrackElement.TrackType == Points)
5145  {
5146  if((EntryPos == 0) || (EntryPos == 2))
5147  {
5148  if(CurrentTrackElement.Attribute == 0)
5149  {
5150  ExitPos = 1;
5151  }
5152  else
5153  {
5154  ExitPos = 3;
5155  }
5156  }
5157  if(EntryPos == 1)
5158  {
5159  if(CurrentTrackElement.Attribute == 0)
5160  {
5161  ExitPos = 0;
5162  }
5163  else
5164  {
5165  Utilities->CallLogPop(720);
5166  return(false);
5167  }
5168  }
5169  if(EntryPos == 3)
5170  {
5171  if(CurrentTrackElement.Attribute == 1)
5172  {
5173  ExitPos = 0;
5174  }
5175  else
5176  {
5177  Utilities->CallLogPop(721);
5178  return(false);
5179  }
5180  }
5181  }
5182  else
5183  {
5184  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
5185  }
5186  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
5187  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
5188  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
5189  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
5190  if(ElementNumber < 2)
5191  {
5192  SkipRouteCheck = true;
5193  }
5194  else
5195  {
5196  SkipRouteCheck = false;
5197  }
5198  if(ElementNumber == 1) // the stop signal
5199  {
5200  RouteStartPosition = CurrentTrackVectorPosition;
5201  }
5202 /*
5203  if(ElementNumber == 2)
5204  {
5205  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
5206  else AutoSigs = false;
5207  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
5208  }
5209 */
5210  if(ElementNumber > 1)
5211  {
5212  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
5213  {
5214  RouteOrPartRouteSet = true;
5215  }
5216  else
5217  {
5218  RouteOrPartRouteSet = false;
5219  }
5220  }
5221  if(!SkipRouteCheck && !RouteOrPartRouteSet)
5222  {
5223  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
5224  {
5225  Utilities->CallLogPop(1859);
5226  return(false);
5227  }
5228  int ExitLink = CurrentTrackElement.Link[ExitPos];
5229  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
5230  {
5231  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
5232  {
5233  Utilities->CallLogPop(1850);
5234  return(false);
5235  }
5236  }
5237  }
5238  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
5239  if(EntryPos < 2)
5240  {
5241  Distance += CurrentTrackElement.Length01;
5242  }
5243  else
5244  {
5245  Distance += CurrentTrackElement.Length23;
5246  }
5247  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
5248  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
5249  CurrentTrackVectorPosition = NextTrackVectorPosition;
5250  EntryPos = NextEntryPos;
5251  ElementNumber++;
5252  } // while(true)
5253 
5254  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
5255  // from the stop signal (note that it may be last in an autosigs route)
5256  // a single element route at the stop signal should have been removed prior to this function being called (that called before
5257  // this in ClockTimer2)
5258 
5259  // now add elements to the CallonVector
5260  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
5261 
5262  AllRoutes->CallonVector.push_back(CallonEntry);
5263  Utilities->CallLogPop(1860);
5264  return(true); // return false if fail to set route for any reason
5265 }
5266 
5267 // ---------------------------------------------------------------------------
5268 /*
5269  bool TTrain::TimetableFinished()
5270  {
5271  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5272  {
5273  return true;
5274  }
5275  return false;
5276  }
5277 */
5278 // ---------------------------------------------------------------------------
5279 
5280 AnsiString TTrain::GetTrainHeadCode(int Caller)
5281 
5282 {
5283  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5284  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5285 
5286  Utilities->CallLogPop(1452);
5287  return(RepeatHeadCode);
5288 }
5289 
5290 // ---------------------------------------------------------------------------
5291 
5292 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5293 {
5294  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5295  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5296 
5297  Utilities->CallLogPop(1453);
5298  return(RepeatTime);
5299 }
5300 
5301 // ---------------------------------------------------------------------------
5302 
5303 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5304 {
5305  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5306  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5307  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5308  // first check that train is fully on the railway
5309  bool FrontValid = false, RearValid = false;
5310  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5311 
5312  if((LeadElement == -1) || (MidElement == -1))
5313  {
5314  TrainToBeJoinedBy = NULL;
5315  Utilities->CallLogPop(2131);
5316  return(false);
5317  }
5319  {
5320  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5321  FrontValid = true;
5322  }
5324  {
5325  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5326  RearValid = true;
5327  }
5328  int TrainToBeJoinedByID = -1;
5329 
5330  // first check if on a 2-track element & select correct ID number if so
5331  if(FrontValid)
5332  {
5333  if(FrontAdjacentTrackElement.TrackType == Bridge)
5334  {
5336  {
5337  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5338  }
5339  else
5340  {
5341  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5342  }
5343  }
5344  else
5345  {
5346  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5347  }
5348  }
5349  if((TrainToBeJoinedByID < 0) && RearValid)
5350  {
5351  // first check if on a 2-track element & select correct ID number if so
5352  if(RearAdjacentTrackElement.TrackType == Bridge)
5353  {
5355  {
5356  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23;
5357  }
5358  else
5359  {
5360  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01;
5361  }
5362  }
5363  else
5364  {
5365  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5366  }
5367  }
5368  if(TrainToBeJoinedByID < 0) // no adjacent train
5369  {
5370  TrainToBeJoinedBy = NULL;
5371  Utilities->CallLogPop(2132);
5372  return(false);
5373  }
5374  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5375  if(!TrainToBeJoinedBy->Stopped())
5376  {
5377  TrainToBeJoinedBy = NULL;
5378  Utilities->CallLogPop(2133);
5379  return(false);
5380  }
5381  Utilities->CallLogPop(2134);
5382  return(true);
5383 }
5384 
5385 // ---------------------------------------------------------------------------
5386 
5387 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, AnsiString SplitDistribution,
5388  TDateTime TimetableNonRepeatTime, bool Warning)
5389 /*
5390  Time = timetable time, the time adjustments for repeat trains is carried out internally
5391  Not all messages need this, if not needed a dummy value is required but not used
5392 
5393  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5394  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5395  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late <-- sent from RemainHere as LogAction not called for terminate
5396  //NB for Frh just give terminated message but without event time - don't use this function
5397  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5398  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5399  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5400  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5401  FrontSplit: 06:05:40: 2F46 split mass%-Power% = 10-50 from front to 3D54 at Old Street 1 minute late
5402  RearSplit: 06:05:40: 2F46 split mass%-Power% = 10-50 from rear to 3D54 at Old Street 1 minute late
5403  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5404  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5405  ChangeDescription: 06:05:40: 2F46 changed its description to 'NewDescription' at Old Street 1 minute late
5406  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5407  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5408  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5409  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5410  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5411  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5412  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5413  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5414  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass stop signal
5415  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5416  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5417  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5418  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5419  SignallerStop 06:05:40: 2F46 stopped on signaller command
5420  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5421  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5422 */{
5423  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5424  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5425  AnsiString BaseLog = "", WarningBaseLog = "", ReminderBaseLog = "", PerfLog = "", ActionLog = "";
5426  int IntMinsLate = 0;
5427  bool TTEvent = false; //indicates a timetabled event, prevents reminders for non-tt events where these set for next tt event
5428  //don't need it for warnings as these passed in from appropriate calling functions
5429  // need to set it in case MinsLate == 0, since it isn't tested for that
5430  if(ActionType == Arrive)
5431  {
5432  ActionLog = " arrived at ";
5433  TTEvent = true;
5434  }
5435  if(ActionType == Terminate) //redundant as Logaction not called for terminate - RemainHere deals with logging for terminate
5436  {
5437  if(TerminatedMessageSent) // to avoid it being sent twice
5438  {
5439  Utilities->CallLogPop(1104);
5440  return;
5441  }
5442  ActionLog = " terminated at ";
5443  TTEvent = true;
5444  TerminatedMessageSent = true;
5445  }
5446  if(ActionType == Depart)
5447  {
5448  ActionLog = " departed from ";
5449  TTEvent = true;
5450  }
5451  if(ActionType == Pass)
5452  {
5453  ActionLog = " passed ";
5454  TTEvent = true;
5455  }
5456  if(ActionType == Create)
5457  {
5458  ActionLog = " created at ";
5459  }
5460  if(ActionType == Enter)
5461  {
5462  ActionLog = " entered railway at ";
5463  }
5464  if(ActionType == ChangeDescription)
5465  {
5466  ActionLog = " changed its description to '" + Description + "' at "; //changed to train description at v2.16.1
5467  TTEvent = true;
5468  }
5469  if(ActionType == Leave)
5470  {
5471  ActionLog = " left railway at ";
5472  TTEvent = true;
5473  }
5474  if(ActionType == FrontSplit)
5475  {
5476  ActionLog = " split mass%-Power% = " + SplitDistribution + " from front to ";
5477  TTEvent = true;
5478  }
5479  if(ActionType == RearSplit)
5480  {
5481  ActionLog = " split mass%-Power% = " + SplitDistribution + " from rear to ";
5482  TTEvent = true;
5483  }
5484  if(ActionType == JoinedByOther)
5485  {
5486  ActionLog = " joined by ";
5487  TTEvent = true;
5488  }
5489  if(ActionType == ChangeDirection)
5490  {
5491  ActionLog = " changed direction at ";
5492  TTEvent = true;
5493  }
5494  if(ActionType == NewService)
5495  {
5496  ActionLog = " became new service ";
5497  TTEvent = true;
5498  }
5499  if(ActionType == TakeSignallerControl)
5500  {
5501  ActionLog = " taken under signaller control at ";
5502  }
5503  if(ActionType == RestoreTimetableControl)
5504  {
5505  ActionLog = " restored to timetable control at ";
5506  }
5507  if(ActionType == RemoveTrain)
5508  {
5509  if(Crashed)
5510  {
5511  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5512  }
5513  else if(Derailed)
5514  {
5515  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5516  }
5517  else
5518  {
5519  ActionLog = " REMOVED FROM RAILWAY at ";
5520  }
5521  }
5522  if(ActionType == SignallerMoveForwards)
5523  {
5524  ActionLog = " received signaller authority to proceed";
5525  }
5526  if(ActionType == SignallerStepForward)
5527  {
5528  ActionLog = " received signaller authority to step forward";
5529  }
5530  if(ActionType == SignallerChangeDirection)
5531  {
5532  ActionLog = " changed direction under signaller control at ";
5533  }
5534  if(ActionType == SignallerPassRedSignal)
5535  {
5536  ActionLog = " received signaller authority to pass stop signal";
5537  }
5538  if(ActionType == SignallerControlStop)
5539  {
5540  ActionLog = " received signaller instruction to stop";
5541  }
5542  if(ActionType == SignallerStop)
5543  {
5544  ActionLog = " stopped on signaller instruction ";
5545  }
5546  if(ActionType == SignallerJoin)
5547  {
5548  ActionLog = " joined under signaller control by ";
5549  }
5550  if(ActionType == TrainFailure)
5551  {
5552  ActionLog = " suffered an onboard power failure at ";
5553  }
5554  if(ActionType == RepairFailedTrain)
5555  {
5556  ActionLog = " failure repaired at ";
5557  }
5558  if(ActionType == SignallerLeave)
5559  {
5560  ActionLog = " left railway under signaller control at ";
5561  }
5562  if(OtherHeadCode != "")
5563  {
5564  OtherHeadCode += " at ";
5565  }
5566  TDateTime ActualTime = TrainController->TTClockTime;
5567 
5568  if(Warning)
5569  {
5570  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5571  WarningBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5572  }
5573  else
5574  {
5575  if(TTEvent && ((ActionVectorEntryPtr->Reminder == 1) || (ActionVectorEntryPtr->Reminder == 4)))
5576  {
5577  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " REMINDER: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5578  ReminderBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5579  }
5580  else if(TTEvent && (ActionVectorEntryPtr->Reminder == 2) && (ActionLog == " departed from ")) //depart only
5581  {
5582  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " REMINDER: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5583  ReminderBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5584  }
5585  else if(TTEvent && (ActionVectorEntryPtr->Reminder == 3) && (ActionLog == " arrived at ")) //arrive only
5586  {
5587  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " REMINDER: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5588  ReminderBaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName; //added time at v2.13.0
5589  }
5590  else
5591  {
5592  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5593  }
5594  }
5595 
5596  bool TimePerformance = true;
5597 
5598  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5599  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5600  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5601  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5602  // SignallerJoin & RepairFailedTrain new at v2.4.0
5603  {
5604  TimePerformance = false;
5605  }
5606  if(TimePerformance)
5607  {
5608  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5609  MinsDelayed = float(MinsLate);
5610  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5611  {
5612  MinsDelayed = 0;
5613  }
5614  // new v2.2.0 for OpActionPanel, can be positive or negative
5615  if(ActionType == Arrive)
5616  {
5618  }
5619  // since train has just arrived this value is the
5620  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5621  // subtracted from later stop recoverable times.
5622  if(MinsLate < 0)
5623  {
5624  IntMinsLate = int(ceil(MinsLate));
5625  }
5626  if(MinsLate > 0)
5627  {
5628  IntMinsLate = int(floor(MinsLate));
5629  }
5630  if(IntMinsLate == 0)
5631  {
5632  PerfLog = " on time";
5633  }
5634  else if(IntMinsLate == 1)
5635  {
5636  PerfLog = " 1 minute late";
5637  }
5638  else if(IntMinsLate == -1)
5639  {
5640  PerfLog = " 1 minute early";
5641  }
5642  else if(IntMinsLate > 1)
5643  {
5644  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5645  }
5646  else if(IntMinsLate < -1)
5647  {
5648  int PosIntMinsLate = -IntMinsLate;
5649  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5650  }
5651  if(LocationName.Pos('-') > 0)
5652  {
5653  PerfLog = "," + PerfLog;
5654  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5655  }
5656  PerfLogForm->PerformanceLog(0, BaseLog + PerfLog);
5657  }
5658  else
5659  {
5660  PerfLogForm->PerformanceLog(1, BaseLog);
5661  }
5662  if(Warning)
5663  {
5664  Display->WarningLog(0, WarningBaseLog);
5665  }
5666  if(ReminderBaseLog != "")
5667  {
5668  Display->WarningLog(24, ReminderBaseLog);
5669  }
5670  // update statistics
5671  if((ActionType == Arrive) && (IntMinsLate == 0))
5672  {
5674  }
5675  else if((ActionType == Arrive) && (IntMinsLate > 0))
5676  {
5678  TrainController->TotLateArrMins += IntMinsLate;
5679  }
5680  else if((ActionType == Arrive) && (IntMinsLate < 0))
5681  {
5683  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5684  }
5685 
5686  else if((ActionType == Pass) && (IntMinsLate == 0))
5687  {
5689  }
5690  else if((ActionType == Pass) && (IntMinsLate > 0))
5691  {
5693  TrainController->TotLatePassMins += IntMinsLate;
5694  }
5695  else if((ActionType == Pass) && (IntMinsLate < 0))
5696  {
5698  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5699  }
5700 
5701  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5702  {
5704  }
5705  else if((ActionType == Leave) && (IntMinsLate > 0))
5706  {
5708  TrainController->TotLateExitMins += IntMinsLate;
5709  }
5710  else if((ActionType == Leave) && (IntMinsLate < 0))
5711  {
5713  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5714  }
5715 
5716  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5717  {
5719  }
5720  else if((ActionType == Depart) && (IntMinsLate > 0))
5721  {
5723  TrainController->TotLateDepMins += IntMinsLate;
5724  }
5725  Utilities->CallLogPop(968);
5726 }
5727 
5728 // ---------------------------------------------------------------------------
5729 
5730 void TTrain::TrainHasFailed(int Caller)
5731 {
5732  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5733  if(Crashed || Derailed || DerailPending)
5734  {
5735  TrainFailurePending = false;
5736  Utilities->CallLogPop(2135);
5737  return;
5738  }
5739  AnsiString LocName = "";
5740 
5741  if(LeadElement > -1)
5742  {
5744  }
5745  if((LocName == "") && (MidElement > -1))
5746  {
5748  }
5749  if((LocName == "") && LeadElement > -1)
5750  {
5751  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5752  }
5753  if((LocName == "") && (MidElement > -1))
5754  {
5755  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5756  }
5757  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5758  TrainFailed = true;
5759  TrainFailurePending = false;
5760  CallingOnFlag = false; //added at v2.10.0
5762  PowerAtRail = 0.08;
5763  AValue = sqrt(2 * PowerAtRail / Mass);
5765  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5766  if(Stopped())
5767  {
5768  EntrySpeed = 0;
5769  ExitSpeedHalf = 0;
5770  ExitSpeedFull = 0;
5771  MaxExitSpeed = 0;
5772  BrakeRate = 0;
5773  StoppedWithoutPower = true;
5774  }
5776  LogAction(33, HeadCode, "", TrainFailure, LocName, "", TDateTime(0), true);
5777  // true for warning, TDateTime not used
5778  Utilities->CallLogPop(2136);
5779 }
5780 
5781 // ---------------------------------------------------------------------------
5782 
5783 void TTrain::FrontTrainSplit(int Caller) //Major rewrite at v2.18.0 using new ThisLocationLongEnoughForSplit
5784 {
5785 /*
5786  Split logic for is:- the final 4 train positions must overlap with the original train, & final 4 positions
5787  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5788  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5789  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5790 */
5791  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5792  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5793 
5794 /* restriction removed at v2.19.0
5795  if(PowerAtRail < 1)
5796  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5797  {
5798  if(!ZeroPowerNoFrontSplitMessage)
5799  {
5800  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5801  }
5802  ZeroPowerNoFrontSplitMessage = true;
5803  Utilities->CallLogPop(2137);
5804  return;
5805  }
5806 */
5807 
5808  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5809 
5810  if(LocationName == "")
5811  {
5812  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5813  }
5814  int RearTrainRearPos, RearTrainFrontPos, RearTrainExitPos;
5815  int FrontTrainRearPos, FrontTrainFrontPos;
5817 
5818  if(LocationName == "")
5819  {
5820  throw Exception("Error - LocationName not set in FrontTrainSplit");
5821  }
5822  // if message given call at ~5 sec intervals in case train repositioned
5823 
5824  bool TemporaryDelay = false;
5826  MidEntryPos, FrontTrainFrontPos, FrontTrainRearPos, RearTrainFrontPos, RearTrainRearPos, TemporaryDelay))
5827  {
5829  {
5831  {
5832  TrainController->StopTTClockMessage(151, HeadCode + " failed to split - location too short at " + LocationName + ", reposition train if possible.");
5833  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5835  }
5836  }
5837  Utilities->CallLogPop(1009); //these were inside above bracket & caused own detected fault on second access as didn't return, moved here at v2.19.0
5838  return;
5839  }
5840 
5841  if(TemporaryDelay)
5842  {
5844  Utilities->CallLogPop(2683);
5845  return;
5846  }
5847 
5848 //it is long enough for split
5849  AnsiString SplitTrainFixedDescription = ActionVectorEntryPtr->LinkedTrainEntryPtr->FixedDescription; //save these for new train before ActionVectorEntryPtr incremented
5850  AnsiString SplittingTrainDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
5851  bool SplitTrainExplicitDescription = ActionVectorEntryPtr->LinkedTrainEntryPtr->ExplicitDescription;
5853 
5854  UnplotTrain(0);
5855  StartSpeed = 0;
5856  RearStartElement = RearTrainRearPos; //this is for the current train, not the new train which will attach to the front of this train
5857  for(int x = 0; x < 4; x++)
5858  {
5859  if(Track->TrackElementAt(1664, RearStartElement).Conn[x] == RearTrainFrontPos)
5860  {
5861  RearStartExitPos = x;
5862  }
5863  }
5864  StoppedAtLocation = true;
5865  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5866  {
5867  StoppedWithoutPower = true;
5868  }
5869  PlotStartPosition(3);
5872 // ActionVectorEntryPtr++; moved lower down at v2.15.0 because of new section below
5874 
5875  //new at v2.15.0 for unequal split in mass & power
5876  int NewTrainMass;
5877  double NewTrainPowerAtRail;
5879  {
5880  int pos = ActionVectorEntryPtr->SplitDistribution.Pos('-');
5881  int MassPercent = ActionVectorEntryPtr->SplitDistribution.SubString(1, pos - 1).ToInt(); //validity checked during validation
5882  int PowerPercent = ActionVectorEntryPtr->SplitDistribution.SubString(pos + 1, ActionVectorEntryPtr->SplitDistribution.Length() - pos).ToInt();
5883  NewTrainMass = Mass * double(MassPercent)/100.0;
5884  Mass = Mass - NewTrainMass;
5885  NewTrainPowerAtRail = PowerAtRail * double(PowerPercent)/100.0;
5886  if(NewTrainPowerAtRail == 0)
5887  {
5888  NewTrainPowerAtRail = 0.08; //min value represents 0
5889  }
5890  PowerAtRail = PowerAtRail - NewTrainPowerAtRail;
5891  AValue = sqrt(2 * PowerAtRail / Mass);
5892  }
5893  else // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5894  {
5895  Mass = Mass / 2;
5896  NewTrainMass = Mass;
5897  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
5898  // MaxBrakeRate = MaxBrakeRate / 2; this was wrong - want brake rate to stay the same, brake force is halved but that not a train parameter
5899  // and when needed it's calculated from rate & mass - changed at v2.15.0
5900  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
5901  PowerAtRail = PowerAtRail / 2;
5902  NewTrainPowerAtRail = PowerAtRail;
5903  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
5904  AValue = sqrt(2 * PowerAtRail / Mass);
5905  // shouldn't change but include in case not set earlier
5906  }
5907 
5908  TActionEventType EventType = NoEvent;
5909  ActionVectorEntryPtr++; //moved here at v2.18.0 to give more chances to split in case points set wrongly initially, also when AddTrain TrainVector
5910  //may be repositioned so all references to this train may be invalid
5911 
5912  if(!TrainController->AddTrain(0, FrontTrainRearPos, FrontTrainFrontPos, OtherHeadCode, 0, NewTrainMass, MaxRunningSpeed, MaxBrakeRate, NewTrainPowerAtRail,
5913  "Timetable", LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5914  // false for SignallerControl
5915  {
5916  Utilities->CallLogPop(1721); // EventType not used here
5917  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5918  // another train, in which case a message will have been sent to the perf log, also might well clear later
5919  // when other train moves away
5920  return;
5921  }
5922 
5923  TrainController->TrainVector.back().Description = SplitTrainFixedDescription; //added at v2.16.1, new train takes description from its TrainDataEntry
5924  if(!SplitTrainExplicitDescription) //new at v2.15.0 see above
5925  {
5926 // OldActionVectorEntryPtr->LinkedTrainEntryPtr->Description = OriginalDescription; dropped at v2.16.1
5927  TrainController->TrainVector.back().Description = SplittingTrainDescription; //else takes it from this train's description
5928  }
5929 
5930  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5931  // see mods in UpdateTrain for v1.3.2
5932  TrainController->TrainAdded = true;
5933 
5934  TTrainOperatingData &TTOD = LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5935 
5936  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5937  TTOD.RunningEntry = Running;
5938  Utilities->CallLogPop(998);
5939 }
5940 
5941 // ---------------------------------------------------------------------------
5942 
5943 void TTrain::RearTrainSplit(int Caller) //Major rewrite at v2.18.0 using new ThisLocationLongEnoughForSplit
5944 {
5945 /*
5946  Split logic for is:- the final 4 train positions must overlap with the original train, & final 4 positions
5947  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5948  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5949  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5950 */
5951  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5952  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5953 
5954 /* restriction removed at v2.19.0
5955  if(PowerAtRail < 1)
5956  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5957  {
5958  if(!ZeroPowerNoRearSplitMessage)
5959  {
5960  TrainController->StopTTClockMessage(176, HeadCode + ": A train without power can't split");
5961  }
5962  ZeroPowerNoRearSplitMessage = true;
5963  Utilities->CallLogPop(2685);
5964  return;
5965  }
5966 */
5967  AnsiString LocationName = Track->TrackElementAt(1676, LeadElement).ActiveTrackElementName;
5968 
5969  if(LocationName == "")
5970  {
5971  LocationName = Track->TrackElementAt(1677, MidElement).ActiveTrackElementName;
5972  }
5973  int RearTrainRearPos, RearTrainFrontPos;
5974  int FrontTrainRearPos, FrontTrainFrontPos, FrontTrainExitPos;
5976 
5977  if(LocationName == "")
5978  {
5979  throw Exception("Error - LocationName not set in RearTrainSplit");
5980  }
5981  // if message given call at ~5 sec intervals in case train repositioned
5982 
5983  bool TemporaryDelay = false;
5985  MidEntryPos, FrontTrainFrontPos, FrontTrainRearPos, RearTrainFrontPos, RearTrainRearPos, TemporaryDelay))
5986  {
5988  {
5990  {
5991  TrainController->StopTTClockMessage(177, HeadCode + " failed to split - location too short at " + LocationName + ", reposition train if possible.");
5992  TrainController->LogActionError(66, HeadCode, "", FailLocTooShort, LocationName);
5994  }
5995  }
5996  Utilities->CallLogPop(2686); //these were inside above bracket & caused own detected fault on second access as didn't return, moved here at v2.19.0
5997  return;
5998  }
5999 
6000  if(TemporaryDelay)
6001  {
6003  Utilities->CallLogPop(2684);
6004  return;
6005  }
6006 
6007 //it is long enough for split
6008  AnsiString SplitTrainFixedDescription = ActionVectorEntryPtr->LinkedTrainEntryPtr->FixedDescription; //save these for new train before ActionVectorEntryPtr incremented
6009  AnsiString SplittingTrainDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6010  bool SplitTrainExplicitDescription = ActionVectorEntryPtr->LinkedTrainEntryPtr->ExplicitDescription;
6012 
6013  UnplotTrain(11);
6014  StartSpeed = 0;
6015  RearStartElement = FrontTrainRearPos; //this is for the current train, not the new train which will attach to the rear of this train
6016  for(int x = 0; x < 4; x++)
6017  {
6018  if(Track->TrackElementAt(1665, RearStartElement).Conn[x] == FrontTrainFrontPos)
6019  {
6020  RearStartExitPos = x;
6021  }
6022  }
6023  StoppedAtLocation = true;
6024  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6025  {
6026  StoppedWithoutPower = true;
6027  }
6028  PlotStartPosition(12);
6031 // ActionVectorEntryPtr++; moved lower down at v2.15.0 because of new section below
6033 
6034  //new at v2.15.0 for unequal split in mass & power
6035  int NewTrainMass;
6036  double NewTrainPowerAtRail;
6038  {
6039  int pos = ActionVectorEntryPtr->SplitDistribution.Pos('-');
6040  int MassPercent = ActionVectorEntryPtr->SplitDistribution.SubString(1, pos - 1).ToInt(); //validity checked during validation
6041  int PowerPercent = ActionVectorEntryPtr->SplitDistribution.SubString(pos + 1, ActionVectorEntryPtr->SplitDistribution.Length() - pos).ToInt();
6042  NewTrainMass = Mass * double(MassPercent)/100.0;
6043  Mass = Mass - NewTrainMass;
6044  NewTrainPowerAtRail = PowerAtRail * double(PowerPercent)/100.0;
6045  if(NewTrainPowerAtRail == 0)
6046  {
6047  NewTrainPowerAtRail = 0.08; //min value represents 0
6048  }
6049  PowerAtRail = PowerAtRail - NewTrainPowerAtRail;
6050  AValue = sqrt(2 * PowerAtRail / Mass);
6051  }
6052  else // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6053  {
6054  Mass = Mass / 2;
6055  NewTrainMass = Mass;
6056  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6057  // MaxBrakeRate = MaxBrakeRate / 2; this was wrong - want brake rate to stay the same, brake force is halved but that not a train parameter
6058  // and when needed it's calculated from rate & mass - changed at v2.15.0
6059  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6060  PowerAtRail = PowerAtRail / 2;
6061  NewTrainPowerAtRail = PowerAtRail;
6062  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6063  AValue = sqrt(2 * PowerAtRail / Mass);
6064  // shouldn't change but include in case not set earlier
6065  }
6066 
6067  TActionEventType EventType = NoEvent;
6068  ActionVectorEntryPtr++; //moved here at v2.18.0 to give more chances to split in case points set wrongly initially, also when AddTrain TrainVector
6069  //may be repositioned so all references to this train may be invalid
6070 
6071  if(!TrainController->AddTrain(4, RearTrainRearPos, RearTrainFrontPos, OtherHeadCode, 0, NewTrainMass, MaxRunningSpeed, MaxBrakeRate, NewTrainPowerAtRail,
6072  "Timetable", LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6073  // false for SignallerControl
6074  {
6075  Utilities->CallLogPop(2687); // EventType not used here
6076  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6077  // another train, in which case a message will have been sent to the perf log, also might well clear later
6078  // when other train moves away
6079  return;
6080  }
6081 
6082  TrainController->TrainVector.back().Description = SplitTrainFixedDescription; //added at v2.16.1, new train takes description from its TrainDataEntry
6083  if(!SplitTrainExplicitDescription) //new at v2.15.0 see above
6084  {
6085 // OldActionVectorEntryPtr->LinkedTrainEntryPtr->Description = OriginalDescription; dropped at v2.16.1
6086  TrainController->TrainVector.back().Description = SplittingTrainDescription; //else takes it from this train's description
6087  }
6088 
6089  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6090  // see mods in UpdateTrain for v1.3.2
6091  TrainController->TrainAdded = true;
6092 
6093  TTrainOperatingData &TTOD = LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6094 
6095  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6096  TTOD.RunningEntry = Running;
6097  Utilities->CallLogPop(2688);
6098 }
6099 
6100 // ---------------------------------------------------------------------------
6101 
6102 void TTrain::FinishJoin(int Caller)
6103 {
6104  if(FinishJoinLogSent == false)
6105  {
6106  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6107  FinishJoinLogSent = true; // so don't keep logging this event
6108  // don't need to reset it to false after the event as the train is deleted
6109  }
6110  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6111  if(TrainFailed)
6112  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6113  {
6115  {
6116  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6117  }
6119  Utilities->CallLogPop(2139);
6120  return;
6121  }
6122  if(TrainGone)
6123  // this means that the train has already joined the other & is awaiting deletion by TrainController
6124  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6125  // on from jbo & TrainToJoinIsAdjacent returns false
6126  {
6127  Utilities->CallLogPop(1035);
6128  return;
6129  }
6130  TTrain *TrainToJoin;
6132 
6133  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6134  {
6136  {
6137  // PerfLogForm->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6140  }
6141  Utilities->CallLogPop(1030);
6142  return; // keep this here in case need to add code before final return
6143  }
6144  // no need to clear error report flag here, cleared in jbo function
6145  // No need to set TimetableFinished, done in jbo function
6146  Utilities->CallLogPop(1031);
6147 }
6148 
6149 // ---------------------------------------------------------------------------
6150 
6151 void TTrain::JoinedBy(int Caller)
6152 {
6153  if(TrainController->OpTimeToActUpdateCounter == 0) //added at v2.13.2. Use OpTimeToActUpdateCounter for convenience so only issue the event log
6154  //once every second rather than many times. Can't use an event logged flag because there may
6155  //be several trains that are to be joined by others
6156  {
6157  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Waiting to be joined");
6158  }
6159  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6160 
6161 /* restriction removed at v2.19.0
6162  if(PowerAtRail < 1)
6163  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6164  {
6165  if(!ZeroPowerNoJoinedByMessage)
6166  {
6167  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6168  }
6169  ZeroPowerNoJoinedByMessage = true;
6170  Utilities->CallLogPop(2140);
6171  return;
6172  }
6173 */
6174  TTrain *TrainToBeJoinedBy;
6176 
6177  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6178  {
6180  {
6181  // PerfLogForm->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6184  }
6185  LastActionDelayFlag = true;
6186  // need to update LastActionTime if this train first to arrive as need 30s after
6187  // both adjacent before the join
6188  Utilities->CallLogPop(1032);
6189  return;
6190  }
6191  // here when other train is adjacent
6193  {
6195  // need to update this as need 30s after both adjacent before the join
6196  LastActionDelayFlag = false;
6197  Utilities->CallLogPop(1033);
6198  return;
6199  }
6200  // here when other train is adjacent & 30 secs elapsed since both adjacent
6201 
6202  // set new values for mass etc
6203  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6204  {
6205  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6206  }
6207  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6208  double OwnBrakeForce = MaxBrakeRate * Mass;
6209  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6210 
6211  Mass += TrainToBeJoinedBy->Mass;
6212  MaxBrakeRate = CombinedBrakeRate;
6213  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6214  AValue = sqrt(2 * PowerAtRail / Mass);
6215 
6217  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6218  TrainToBeJoinedBy->TimetableFinished = true;
6219  TrainToBeJoinedBy->TrainGone = true;
6220  TrainController->LogEvent("" + AnsiString(Caller) + "," + HeadCode + ",Joined By," + FJOHeadCode); //added at v2.13.2 to provide more information
6221  // this will cause other train to be deleted
6222  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6226  Utilities->CallLogPop(1034);
6227 }
6228 
6229 // ---------------------------------------------------------------------------
6230 
6231 void TTrain::ChangeTrainDirection(int Caller, bool NoLogFlag)
6232 {
6233  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6234  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6235 
6236  /* restriction removed at v2.19.0
6237  if(PowerAtRail < 1)
6238  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6239  {
6240  if(!ZeroPowerNoCDTMessage)
6241  {
6242  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6243  }
6244  ZeroPowerNoCDTMessage = true;
6245  Utilities->CallLogPop(2141);
6246  return;
6247  }
6248 */
6249  TColor TempColour = BackgroundColour;
6250 
6251  UnplotTrain(2);
6254  StartSpeed = 0;
6255  StoppedAtLocation = true;
6256  PlotStartPosition(1);
6257  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6258  // plot same as was - should always be pale green
6259  if(!NoLogFlag)
6260  {
6263  }
6265 
6266  //now erase a stub route if there is one, added at v2.5.1
6267  //first element of route is now immediately behind the train (i.e. next to MidElement)
6268  if(MidEntryPos >= 0)
6269  {
6270  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6271  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6272  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6273  int RouteNumber = -1;
6274  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6275  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6276  {
6277  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6278  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
6279  //elements can continue to be removed from that route
6280  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6281 // if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6282  { //above condition removed v2.17.0 so non-facing signal or continuation doesn't stop route being removed
6283  //if it is a facing signal then it will be detected below and not removed
6284  bool FirstPass = true; //added at v2.8.0
6285  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6286  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
6287  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6288  int TVPos2 = PDE.GetTrackVectorPosition();
6289  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6290  {
6291  break;
6292  }
6293  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6295  {
6296  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6297  }
6298  else
6299  {
6300  break;
6301  }
6302  FirstPass = false;
6303  }
6304  AllRoutes->RebuildRailwayFlag = true;
6305  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6306  }
6307  }
6308  }
6309  Utilities->CallLogPop(1012);
6310 }
6311 
6312 // ---------------------------------------------------------------------------
6313 
6314 void TTrain::NewTrainService(int Caller, bool NoLogFlag) //, bool NoLogFlag added at v2.12.0 for new service tt skips
6315 // change to new train, give new service message
6316 //same RepeatNumber used for the new service
6317 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6318  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6319  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6320 
6321 /* restriction removed at v2.19.0
6322  if(PowerAtRail < 1)
6323  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6324  {
6325  if(!ZeroPowerNoNewServiceMessage)
6326  {
6327  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6328  }
6329  ZeroPowerNoNewServiceMessage = true;
6330  Utilities->CallLogPop(2142);
6331  return;
6332  }
6333 */
6335 
6336  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6337 
6338  if(!NoLogFlag)
6339  {
6341  }
6342  UnplotTrain(3);
6345  StartSpeed = 0;
6350  HeadCode = NewHeadCode;
6352  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6353  {
6354  Description = OriginalDescription; //changed at v2.16.1 to train description
6355  }
6356  StoppedAtLocation = true;
6357  PlotStartPosition(5);
6359  // pale green
6362  TerminatedMessageSent = false;
6363  Utilities->CallLogPop(1022);
6364 }
6365 
6366 // ---------------------------------------------------------------------------
6367 
6368 void TTrain::RemainHere(int Caller) //added warnings & reminders at v2.19.0 (not sent from LogAction as not called when train terminates)
6369 {
6370  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6371  if(RemainHereLogNotSent) // to prevent repeated logs
6372  {
6373  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6374  RemainHereLogNotSent = false;
6375  }
6377  {
6378  TDateTime ActualTime = TrainController->TTClockTime;
6379  AnsiString BaseLog = "", Location = ActionVectorEntryPtr->LocationName;
6380  AnsiString PerfLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + " terminated at " + Location;
6382  {
6383  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + " terminated at " + Location;
6384  Display->WarningLog(25, PerfLog);
6385  PerfLogForm->PerformanceLog(65, BaseLog);
6386  }
6387  else if(ActionVectorEntryPtr->Reminder > 0)
6388  {
6389  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " REMINDER: " + HeadCode + " terminated at " + Location;
6390  Display->WarningLog(26, PerfLog);
6391  PerfLogForm->PerformanceLog(66, BaseLog);
6392  }
6393  else
6394  {
6395  PerfLogForm->PerformanceLog(67, PerfLog);
6396  }
6398  TerminatedMessageSent = true;
6399  }
6400  TimetableFinished = true;
6401  Utilities->CallLogPop(1023);
6402 }
6403 
6404 // ---------------------------------------------------------------------------
6405 
6406 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6407 /*
6408  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6409  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6410  except where an action is a departure, starting at the current value for the pointer
6411  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6412  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6413 */{
6414  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6415  {
6416  return; // if remove train that starts under signaller control no messages needed
6417 
6418  }
6419  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6420  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6421  if(IncNum > 0)
6422  {
6423  for(int x = 0; x < IncNum; x++)
6424  {
6425  if(x > 0)
6426  {
6427  Ptr++;
6428  }
6429  // arrival - no need to test for termination as this section only covers missed actions up to the
6430  // arrival point - may terminate later but that not missed
6431  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6432  {
6434  }
6435  // arrival & departure
6436  if(Ptr->FormatType == TimeTimeLoc)
6437  {
6439  }
6440  // departure
6441  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6442  {
6443  continue; // skip TimeLoc departures, message given for arrivals
6444  }
6445  // pass
6446  else if(Ptr->FormatType == PassTime)
6447  {
6449  }
6450  // split
6451  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6452  {
6454  }
6455  // jbo
6456  else if(Ptr->Command == "jbo")
6457  {
6459  }
6460  // dsc
6461  else if(Ptr->Command == "dsc") //new at v2.15.0
6462  {
6464  }
6465  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6466  // be starts, finishes or cdt
6467  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6468  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6469  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6470  (Ptr->FormatType == Repeat))
6471  {
6472  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6473  }
6474  }
6475  }
6476  else
6477  {
6478  bool IncludeFER = false;
6479  if(IncNum == -1)
6480  {
6481  IncludeFER = true;
6482  }
6483  while(true) // finish commands & repeats break out of loop
6484  {
6485  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6486  if(!IncludeFER && (Ptr->Command == "Fer"))
6487  {
6488  break;
6489  }
6490  // Fer & included
6491  else if(IncludeFER && (Ptr->Command == "Fer"))
6492  {
6494  break;
6495  }
6496  // Repeat
6497  else if(Ptr->FormatType == Repeat)
6498  {
6499  break;
6500  }
6501  // Fjo
6502  else if(Ptr->Command == "Fjo")
6503  {
6505  break;
6506  }
6507  // Frh
6508  else if(Ptr->Command == "Frh")
6509  {
6511  {
6513  TerminatedMessageSent = true;
6514  }
6515  break;
6516  }
6517  // Frh-sh
6518  else if(Ptr->Command == "Frh-sh")
6519  {
6521  {
6523  TerminatedMessageSent = true;
6524  }
6525  break;
6526  }
6527  // Fns, F-nshs, Fns-sh
6528  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6529  {
6531  break;
6532  }
6533  // end of breakout actions
6534  // arrival
6535  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6536  {
6537  if(IsTrainTerminating(1))
6538  {
6540  TerminatedMessageSent = true;
6541  }
6542  else
6543  {
6545  }
6546  }
6547  // arrival & departure
6548  else if(Ptr->FormatType == TimeTimeLoc)
6549  {
6551  }
6552  // departure
6553  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6554  {
6555  Ptr++;
6556  continue; // skip TimeLoc departures, message given for arrivals
6557  }
6558  // pass
6559  else if(Ptr->FormatType == PassTime)
6560  {
6562  }
6563  // split
6564  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6565  {
6567  }
6568  // jbo
6569  else if(Ptr->Command == "jbo")
6570  {
6572  }
6573  // dsc
6574  else if(Ptr->Command == "dsc") //new at v2.15.0
6575  {
6576 // TrainController->LogActionError(65, HeadCode, "", FailMissedDSC, Ptr->LocationName); don't count as a missed event
6577  }
6578  // cdt
6579  else if(Ptr->Command == "cdt")
6580  {
6581 // TrainController->LogActionError(25, HeadCode, "", FailMissedChangeDirection, Ptr->LocationName); //commented out at v2.12.0 as cdts not counted as missed events
6582  }
6583  // Errors
6584  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6585  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6586  {
6587  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6588  }
6589  Ptr++;
6590  }
6591  TimetableFinished = true;
6592  }
6593  Utilities->CallLogPop(1021);
6594 }
6595 
6596 // ---------------------------------------------------------------------------
6597 
6598 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6599 // ensure same repeatnumber
6600 {
6601  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6603 
6604  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6605  {
6606  Utilities->CallLogPop(1024);
6607  return(false);
6608  }
6609  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6610  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6611  {
6612  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6613  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6614  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6615  {
6616  Utilities->CallLogPop(1025);
6617  return(true);
6618  }
6619  }
6620  Utilities->CallLogPop(1026);
6621  return(false);
6622 }
6623 
6624 // ---------------------------------------------------------------------------
6625 
6626 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6627 // ensure same repeatnumber
6628 {
6629  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6630  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6631 
6632  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6633  {
6634  Utilities->CallLogPop(1027);
6635  return(false);
6636  }
6637  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6638  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6639  {
6640  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6641  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6642  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6643  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6644  {
6645  Utilities->CallLogPop(1028);
6646  return(true);
6647  }
6648  }
6649  Utilities->CallLogPop(1029);
6650  return(false);
6651 }
6652 
6653 // ---------------------------------------------------------------------------
6654 
6655 void TTrain::NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6656 { //same RepeatNumber (i.e. 0) used for the new shuttle
6657 //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6658  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6659  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6660 
6661 /* restriction removed at v2.19.0
6662  if(PowerAtRail < 1)
6663  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6664  {
6665  if(!ZeroPowerNoNewShuttleFromNonRepeatMessage)
6666  {
6667  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6668  }
6669  ZeroPowerNoNewShuttleFromNonRepeatMessage = true;
6670  Utilities->CallLogPop(2143);
6671  return;
6672  }
6673 */
6674  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6675  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6676 
6677  if(!NoLogFlag)
6678  {
6680  }
6681  UnplotTrain(4);
6684  StartSpeed = 0;
6689  HeadCode = NewHeadCode;
6691  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6692  {
6693  Description = OriginalDescription; //changed at v2.16.1 to train description
6694  }
6695  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6696  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6697  StoppedAtLocation = true;
6698  PlotStartPosition(6);
6700  // pale green
6703  TerminatedMessageSent = false;
6704  Utilities->CallLogPop(1078);
6705 }
6706 
6707 // ---------------------------------------------------------------------------
6708 
6709 void TTrain::RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6710 // need to check whether all repeats finished or not
6711 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6712  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6713  if(RemainHereLogNotSent) // to prevent repeated logs
6714  {
6715  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6716  RemainHereLogNotSent = false;
6717  }
6719  // finished all repeats
6720  {
6722  { //no need to suppress this LogAction because BecomeNewService won't be available in this case
6725  TerminatedMessageSent = true;
6726  // no need to clear message as no more actions
6727  }
6728  TimetableFinished = true;
6729  Utilities->CallLogPop(1080);
6730  return;
6731  }
6732 
6733 /* restriction removed at v2.19.0
6734  if(PowerAtRail < 1)
6735  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6736  {
6737  if(!ZeroPowerNoRepeatShuttleMessage)
6738  {
6739  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6740  }
6741  ZeroPowerNoRepeatShuttleMessage = true;
6742  Utilities->CallLogPop(2144);
6743  return;
6744  }
6745 */
6746  int TempRepeatNumber = RepeatNumber + 1;
6747  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6748  // until after LogAction or the wrong time will be used
6749  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6750  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6751 
6752  if(!NoLogFlag)
6753  {
6755  }
6756  RepeatNumber++;
6757  UnplotTrain(5);
6760  StartSpeed = 0;
6765  HeadCode = NewHeadCode;
6767  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6768  {
6769  Description = OriginalDescription; //changed at v2.16.1 to train description
6770  }
6771  StoppedAtLocation = true;
6772  PlotStartPosition(7);
6774  // pale green
6777  TerminatedMessageSent = false;
6778  Utilities->CallLogPop(1079);
6779 }
6780 
6781 // ---------------------------------------------------------------------------
6782 
6783 void TTrain::RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips
6784 { //Note that CumulativeDelayedRandMinsOneTrain carried forward from earlier train (parameter added at v2.13.0)
6785  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6786  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6787 
6788 /* restriction removed at v2.19.0
6789  if(PowerAtRail < 1)
6790  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6791  {
6792  if(!ZeroPowerNoRepeatShuttleOrNewServiceMessage)
6793  {
6794  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6795  }
6796  ZeroPowerNoRepeatShuttleOrNewServiceMessage = true;
6797  Utilities->CallLogPop(2145);
6798  return;
6799  }
6800 */
6802  // finished all repeats
6803  {
6804  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6805  if(!NoLogFlag)
6806  {
6808  }
6809  RepeatNumber = 0;
6810  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6811  UnplotTrain(6);
6814  StartSpeed = 0;
6816  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6817  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6819  HeadCode = NewHeadCode;
6821  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6822  {
6823  Description = OriginalDescription; //changed at v2.16.1 to train description
6824  }
6825  StoppedAtLocation = true;
6826  PlotStartPosition(9);
6830  TerminatedMessageSent = false;
6831  Utilities->CallLogPop(1081);
6832  return;
6833  }
6834  int TempRepeatNumber = RepeatNumber + 1;
6835  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6836  // until after LogAction or the wrong time will be used
6837  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6838  AnsiString OriginalDescription = Description; //new at v2.15.0 to record earlier service description & changed at v2.16.1 to train description
6839 
6840  if(!NoLogFlag)
6841  {
6843  }
6844  RepeatNumber++;
6845  UnplotTrain(7);
6848  StartSpeed = 0;
6853  HeadCode = NewHeadCode;
6855  if(!TrainDataEntryPtr->ExplicitDescription) //new at v2.15.0 see above
6856  {
6857  Description = OriginalDescription; //changed at v2.16.1 to train description
6858  }
6859  StoppedAtLocation = true;
6860  PlotStartPosition(8);
6862  // pale green
6865  TerminatedMessageSent = false;
6866  Utilities->CallLogPop(1082);
6867 }
6868 
6869 // ---------------------------------------------------------------------------
6870 
6872 {
6873  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6874  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6875  // must be preceded by a TimeLoc departure
6876  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6877  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6878  {
6880  {
6881  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6882  {
6883  Utilities->CallLogPop(1083);
6884  return(false);
6885  }
6886  else if((ActionVectorEntryPtr + x)->SequenceType == FinishSequence)
6887  {
6888  Utilities->CallLogPop(1084);
6889  return(true);
6890  }
6891  }
6892  }
6893  Utilities->CallLogPop(1085);
6894  return(false);
6895 }
6896 
6897 // ---------------------------------------------------------------------------
6898 
6899 bool TTrain::AbleToMove(int Caller)
6900 {
6901  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6902  bool Able = true;
6903 
6904  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0
6905  {
6906  // StoppedForTrainInFront removed as tested below
6907  Utilities->CallLogPop(2146); // added v2.4.0
6908  return(false); // added v2.4.0
6909  }
6910  if(LeadElement > -1)
6911  {
6912  if(Track->TrackElementAt(801, LeadElement).TrackType == Buffers) //moved up here from 'else' below at v2.12.0
6913  {
6914  StoppedForTrainInFront = false;
6915  TrainInFront = false;
6916  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6917  Utilities->CallLogPop(2456);
6918  return(false);
6919  }
6920  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6921  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6922  if((FrontPos > -1) && (TrainMode == Signaller) && TrainInFront) //check if train in front still there
6923  {
6924  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6925  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6926  {
6927  Able = true;
6928  TrainInFront = false;
6929  }
6930  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 == -1))
6931  {
6932  Able = true;
6933  TrainInFront = false;
6934  }
6935  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 == -1))
6936  {
6937  Able = true;
6938  TrainInFront = false;
6939  }
6940  else
6941  {
6942  Able = false; //added at v2.12.0 as train still in front so don't want signaller popup options to move available
6943  }
6944  }
6945  }
6946  else // leaving at a continuation so keep going
6947  {
6948  Able = true;
6949  StoppedForTrainInFront = false;
6950  TrainInFront = false;
6951  }
6952  Utilities->CallLogPop(1454);
6953  return(Able);
6954 }
6955 
6956 // ---------------------------------------------------------------------------
6957 
6959 {
6960  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6961  // won't be set; if there is a train then set StoppedForTrainInFront
6962  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignalOrTrainInFront" + "," + HeadCode);
6963  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6964  if(LeadElement == -1) // exiting at continuation
6965  {
6966  Utilities->CallLogPop(2045);
6967  return(false);
6968  }
6969  // end of addition
6970  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6971  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6972 
6973  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6974  {
6975  TrainInFront = true;
6976  Utilities->CallLogPop(1455);
6977  return(false);
6978  }
6979  else
6980  {
6981  Utilities->CallLogPop(1456);
6983  // StoppedWithoutPower added v2.4.0
6984  }
6985 }
6986 
6987 // ---------------------------------------------------------------------------
6988 
6990 {
6991  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6992  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6993  TColor TempColour = BackgroundColour;
6994 
6995  UnplotTrain(8);
6998  StartSpeed = 0;
6999  PlotStartPosition(2);
7000  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
7001 
7002  //now erase a stub route if there is one, added at v2.5.1
7003  //first element of route is now immediately behind the train (i.e. next to MidElement)
7004  if(MidEntryPos >= 0)
7005  {
7006  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
7007  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
7008  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
7009  int RouteNumber = -1;
7010  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
7011  if(RouteType == TAllRoutes::NotAutoSigsRoute)
7012  {
7013  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
7014  int CorrectRouteID = OR.RouteID; //added at v2.13.0 as when last element removed & route removed from vector OR becomes the next route after the erased one and
7015  //elements can continue to be removed from that route
7016  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
7017 // if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
7018  { //above condition removed at v2.17.0 so non-facing signal or continuation doesn't stop route being removed
7019  //if it is a facing signal then it will be detected below and not removed
7020  bool FirstPass = true; //added at v2.8.0
7021  while((OR.PrefDirSize() > 0) && (OR.RouteID == CorrectRouteID)) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
7022  { // && (OR.RouteID == RouteID) added at v2.13.0 to prevent another route having elements removed
7023  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
7024  int TVPos2 = PDE.GetTrackVectorPosition();
7025  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
7026  {
7027  break;
7028  }
7029  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
7031  {
7032  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
7033  }
7034  else
7035  {
7036  break;
7037  }
7038  FirstPass = false;
7039  }
7040  AllRoutes->RebuildRailwayFlag = true;
7041  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
7042  }
7043  }
7044  }
7045  Utilities->CallLogPop(1102);
7046 }
7047 
7048 // ---------------------------------------------------------------------------
7049 
7051 {
7052  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7053  ",FloatingLabelNextString" + "," + HeadCode);
7054  AnsiString RetStr = "", LocationName = "";
7055  //record action time - may be arrival, departure or event for use later (added at v2.13.2)
7056  TDateTime ActionTime = Ptr->ArrivalTime;
7057  if(ActionTime == TDateTime(-1))
7058  {
7059  ActionTime = Ptr->DepartureTime;
7060  }
7061  if(ActionTime == TDateTime(-1))
7062  {
7063  ActionTime = Ptr->EventTime;
7064  }
7065  //If ActionTime still TDateTime(-1) then the train has terminated and 'None...' will be returned
7066  //Now correct it for repeats
7067  if(ActionTime != TDateTime(-1))
7068  {
7069  ActionTime = GetTrainTime(64, ActionTime);
7070  }
7071  if(int(DelayedRandMins) > 0)
7072  {
7073  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7074  {
7075  throw Exception("Error - start entry in FloatingLabelNextString");
7076  }
7077  if(Ptr->FormatType == TimeTimeLoc)
7078  {
7079  if(TrainMode == Timetable)
7080  {
7081  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
7082  // not arrived yet in tt mode
7083  {
7084  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7085  }
7086  else
7087  {
7088  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7089  }
7090  }
7091  else // TrainMode == Signaller
7092  {
7093  if(!DepartureTimeSet) // not arrived yet
7094  {
7095  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7096  }
7097  else
7098  {
7099  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7100  }
7101  }
7102  }
7103  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7104  {
7105  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime + TDateTime(DelayedRandMins/1440)));
7106  }
7107  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7108  {
7109  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7110  }
7111  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7112  {
7113  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(46, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7114  }
7115  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7116  {
7117  RetStr = "Pass " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7118  }
7119  else if(Ptr->Command == "Fns")
7120  {
7121  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7122  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7123  RetStr = GetNewServiceDepartureInfo(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7124  }
7125  else if(Ptr->Command == "F-nshs")
7126  {
7127  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7128  Utilities->Format96HHMM(GetTrainTime(32, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7129  RetStr = GetNewServiceDepartureInfo(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7130  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7131  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7132  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7133  }
7134  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7135  {
7136  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7137  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7138  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7139  RetStr = GetNewServiceDepartureInfo(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7140  }
7141  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7142  {
7143  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7144  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7145  RetStr = GetNewServiceDepartureInfo(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7146  }
7147  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7148  {
7149  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7150  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7151  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7152  RetStr = GetNewServiceDepartureInfo(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7153  }
7154  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7155  {
7156  RetStr ="None, train terminated at " + Ptr->LocationName;
7157  }
7158  else if(Ptr->Command == "Frh")
7159  {
7160  RetStr = "None, train terminated at " + Ptr->LocationName;
7161  }
7162  else if(Ptr->Command == "Fer")
7163  {
7164  AnsiString AllowedExits = "";
7165  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at approx. " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime + TDateTime(DelayedRandMins/1440))) + AllowedExits;
7166  }
7167  else if(Ptr->Command == "Fjo")
7168  {
7169  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at approx. " +
7170  Utilities->Format96HHMM(GetTrainTime(11, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7171  }
7172  else if(Ptr->Command == "jbo")
7173  {
7174  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7175  " at approx. " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7176  }
7177  else if(Ptr->Command == "fsp")
7178  {
7179  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7180  " at approx. " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7181  }
7182  else if(Ptr->Command == "rsp")
7183  {
7184  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7185  " at approx. " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7186  }
7187  else if(Ptr->Command == "cdt")
7188  {
7189  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7190  }
7191  else if(Ptr->Command == "dsc")
7192  {
7193  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(65, Ptr->EventTime + TDateTime(DelayedRandMins/1440)));
7194  }
7195  }
7196  else if(TrainController->TTClockTime > ActionTime) //condition added at v2.13.2 for trains that are delayed other than suffering a random delay
7197  {
7198  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7199  {
7200  throw Exception("Error - start entry in FloatingLabelNextString where TTClockTime > ActionTime");
7201  }
7202  if(Ptr->FormatType == TimeTimeLoc)
7203  {
7204  if(TrainMode == Timetable)
7205  {
7206  if(!TrainAtLocation(4, LocationName) || (LocationName != Ptr->LocationName))
7207  // not arrived yet in tt mode
7208  {
7209  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7210  }
7211  else
7212  {
7213  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7214  }
7215  }
7216  else // TrainMode == Signaller
7217  {
7218  if(!DepartureTimeSet) // not arrived yet
7219  {
7220  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7221  }
7222  else
7223  {
7224  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7225  }
7226  }
7227  }
7228  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7229  {
7230  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7231  }
7232  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7233  {
7234  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);
7235  }
7236  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7237  {
7238  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7239  }
7240  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7241  {
7242  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7243  }
7244  else if(Ptr->Command == "Fns")
7245  {
7246  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(60, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7247  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7248  RetStr = GetNewServiceDepartureInfo(19, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7249  }
7250  else if(Ptr->Command == "F-nshs")
7251  {
7252  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7254  RetStr = GetNewServiceDepartureInfo(20, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7255  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7256  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7257  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7258  }
7259  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7260  {
7261  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(61, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7262  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7263  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7264  RetStr = GetNewServiceDepartureInfo(21, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7265  }
7266  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7267  {
7268  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7269  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7270  RetStr = GetNewServiceDepartureInfo(22, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7271  }
7272  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7273  {
7274  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(62, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7275  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7276  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7277  RetStr = GetNewServiceDepartureInfo(23, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7278  }
7279  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7280  {
7281  RetStr ="None, train terminated at " + Ptr->LocationName;
7282  }
7283  else if(Ptr->Command == "Frh")
7284  {
7285  RetStr = "None, train terminated at " + Ptr->LocationName;
7286  }
7287  else if(Ptr->Command == "Fer")
7288  {
7289  AnsiString AllowedExits = "";
7290  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(5, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime)*/ + AllowedExits;
7291  }
7292  else if(Ptr->Command == "Fjo")
7293  {
7294  RetStr = "Join " + TrainController->GetRepeatHeadCode(63, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7295 // Utilities->Format96HHMM(TrainController->TTClockTime);
7296  }
7297  else if(Ptr->Command == "jbo")
7298  {
7299  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(64, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7300 // " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7301  }
7302  else if(Ptr->Command == "fsp")
7303  {
7304  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(65, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7305  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7306  }
7307  else if(Ptr->Command == "rsp")
7308  {
7309  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(66, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7310  " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7311  }
7312  else if(Ptr->Command == "cdt")
7313  {
7314  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7315  }
7316  else if(Ptr->Command == "dsc")
7317  {
7318  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(TrainController->TTClockTime);
7319  }
7320  }
7321  else //train not delayed
7322  {
7323  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7324  {
7325  throw Exception("Error - start entry in FloatingLabelNextString in final 'else'");
7326  }
7327  if(Ptr->FormatType == TimeTimeLoc)
7328  {
7329  if(TrainMode == Timetable)
7330  {
7331  if(!TrainAtLocation(3, LocationName) || (LocationName != Ptr->LocationName))
7332  // not arrived yet in tt mode
7333  {
7334  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(48, Ptr->ArrivalTime));
7335  }
7336  else
7337  {
7338  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(3, Ptr->DepartureTime));
7339  }
7340  }
7341  else // TrainMode == Signaller
7342  {
7343  if(!DepartureTimeSet) // not arrived yet
7344  {
7345  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(49, Ptr->ArrivalTime));
7346  }
7347  else
7348  {
7349  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(36, Ptr->DepartureTime));
7350  }
7351  }
7352  }
7353  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7354  {
7355  RetStr = "Arrive " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(50, Ptr->ArrivalTime));
7356  }
7357  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7358  {
7359  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(5, Ptr->DepartureTime));
7360  }
7361  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7362  {
7363  RetStr = "Depart " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(51, Ptr->EventTime));
7364  }
7365  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7366  {
7367  RetStr = "Pass " + Ptr->LocationName;// + " at approx. " + Utilities->Format96HHMM(GetTrainTime(52, Ptr->EventTime));
7368  }
7369  else if(Ptr->Command == "Fns")
7370  {
7371  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(53, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7372  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(53, Ptr->EventTime));
7373  RetStr = GetNewServiceDepartureInfo(10, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7374  }
7375  else if(Ptr->Command == "F-nshs")
7376  {
7377  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at approx. " +
7379  RetStr = GetNewServiceDepartureInfo(12, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7380  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7381  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7382  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7383  }
7384  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7385  {
7386  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(54, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7387  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(55, Ptr->EventTime));
7388  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7389  RetStr = GetNewServiceDepartureInfo(14, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7390  }
7391  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7392  {
7393  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7394  +" at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(56, Ptr->EventTime));
7395  RetStr = GetNewServiceDepartureInfo(16, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7396  }
7397  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7398  {
7399  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(55, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7400  Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(57, Ptr->EventTime));
7401  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7402  RetStr = GetNewServiceDepartureInfo(18, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr, false); //if there is a next service this adds the new service departure time to RetStr
7403  }
7404  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7405  {
7406  RetStr ="None, train terminated at " + Ptr->LocationName;
7407  }
7408  else if(Ptr->Command == "Frh")
7409  {
7410  RetStr = "None, train terminated at " + Ptr->LocationName;
7411  }
7412  else if(Ptr->Command == "Fer")
7413  {
7414  AnsiString AllowedExits = "";
7415  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(4, Ptr->ExitList, AllowedExits) /*+ " at approx. " + Utilities->Format96HHMM(GetTrainTime(62, Ptr->EventTime))*/ + AllowedExits;
7416  }
7417  else if(Ptr->Command == "Fjo")
7418  {
7419  RetStr = "Join " + TrainController->GetRepeatHeadCode(56, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// + " at approx. " +
7420 // Utilities->Format96HHMM(GetTrainTime(58, Ptr->EventTime));
7421  }
7422  else if(Ptr->Command == "jbo")
7423  {
7424  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(57, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;// +
7425 // " at approx. " + Utilities->Format96HHMM(GetTrainTime(59, Ptr->EventTime));
7426  }
7427  else if(Ptr->Command == "fsp")
7428  {
7429  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(58, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7430  " at approx. " + Utilities->Format96HHMM(GetTrainTime(60, Ptr->EventTime));
7431  }
7432  else if(Ptr->Command == "rsp")
7433  {
7434  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(59, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7435  " at approx. " + Utilities->Format96HHMM(GetTrainTime(61, Ptr->EventTime));
7436  }
7437  else if(Ptr->Command == "cdt")
7438  {
7439  RetStr = "Change direction at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(63, Ptr->EventTime));
7440  }
7441  else if(Ptr->Command == "dsc")
7442  {
7443  RetStr = "Change description at " + Ptr->LocationName + " at approx. " + Utilities->Format96HHMM(GetTrainTime(66, Ptr->EventTime));
7444  }
7445  }
7446  Utilities->CallLogPop(1124);
7447  return(RetStr);
7448 }
7449 
7450 // ---------------------------------------------------------------------------
7451 /* as was
7452 AnsiString TTrain::FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
7453 {
7454  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7455  ",FloatingLabelNextString" + "," + HeadCode);
7456  AnsiString RetStr = "", LocationName = "";
7457 
7458  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
7459  {
7460  throw Exception("Error - start entry in FloatingLabelNextString");
7461  }
7462  if(Ptr->FormatType == TimeTimeLoc)
7463  {
7464  if(TrainMode == Timetable)
7465  {
7466  if(!TrainAtLocation(, LocationName) || (LocationName != Ptr->LocationName))
7467  // not arrived yet in tt mode
7468  {
7469  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7470  }
7471  else
7472  {
7473  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7474  }
7475  }
7476  else // TrainMode == Signaller
7477  {
7478  if(!DepartureTimeSet) // not arrived yet
7479  {
7480  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7481  }
7482  else
7483  {
7484  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7485  }
7486  }
7487  }
7488  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7489  {
7490  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->ArrivalTime));
7491  }
7492  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7493  {
7494  RetStr = "Depart " + Ptr->LocationName + " approx. " + Utilities->Format96HHMM(ReleaseTime);//GetTrainTime(, Ptr->DepartureTime));
7495  }
7496  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7497  {
7498  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7499  }
7500  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7501  {
7502  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7503  }
7504  else if(Ptr->Command == "Fns")
7505  {
7506  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7507  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7508  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7509  }
7510  else if(Ptr->Command == "F-nshs")
7511  {
7512  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7513  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7514  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7515  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7516  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7517  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7518  }
7519  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7520  {
7521  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7522  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7523  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7524  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7525  }
7526  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7527  {
7528  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7529  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7530  RetStr = GetNewServiceDepartureInfo(, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7531  }
7532  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7533  {
7534  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7535  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7536  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7537  RetStr = GetNewServiceDepartureInfo(, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7538  }
7539  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7540  {
7541  RetStr ="None, train terminated at " + Ptr->LocationName;
7542  }
7543  else if(Ptr->Command == "Frh")
7544  {
7545  RetStr = "None, train terminated at " + Ptr->LocationName;
7546  }
7547  else if(Ptr->Command == "Fer")
7548  {
7549  AnsiString AllowedExits = "";
7550  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime)) + AllowedExits;
7551  }
7552  else if(Ptr->Command == "Fjo")
7553  {
7554  RetStr = "Join " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7555  Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7556  }
7557  else if(Ptr->Command == "jbo")
7558  {
7559  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7560  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7561  }
7562  else if(Ptr->Command == "fsp")
7563  {
7564  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7565  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7566  }
7567  else if(Ptr->Command == "rsp")
7568  {
7569  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7570  " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7571  }
7572  else if(Ptr->Command == "cdt")
7573  {
7574  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(, Ptr->EventTime));
7575  }
7576  Utilities->CallLogPop();
7577  return(RetStr);
7578 }
7579 */
7580 // ---------------------------------------------------------------------------
7581 
7582 AnsiString TTrain::GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
7583 { //last bool added at v2.13.2 so departure info adds random delay if actual rather than not timetable time required
7584  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7585  + AnsiString(RptNum) + ",GetNewServiceDepartureInfo," + HeadCode);
7586  AnsiString DepTime = "", EventTime = "";
7587  bool CDTFlag = false; //reports if train changes direction before departs
7588  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7589  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
7590  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
7591  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
7592  {
7593  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
7594  {
7595  TowardsLocation = AVI->LocationName;
7596  }
7597  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
7598  {
7599  TTrackElement TE = Track->TrackElementAt(1452, (AVI->ExitList.front()));
7600  if(TE.ActiveTrackElementName != "")
7601  {
7602  TowardsLocation = TE.ActiveTrackElementName;
7603  }
7604  else
7605  {
7606  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
7607  }
7608  }
7609  }
7610 
7611  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7612  {
7613  if(AVI->Command == "cdt")
7614  {
7615  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7616  continue;
7617  }
7618  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7619  {
7620  TDateTime TTTime = TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes);
7621  if((DelayedRandMins >= 1) && !TimetableTime)
7622  {
7623  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7624  }
7625  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7626  {
7628  }
7629  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7630  {
7631  EventTime = Utilities->Format96HHMM(TTTime);
7632  }
7633  RetStr += "\nNew service splits at approx. " + EventTime;
7634  Utilities->CallLogPop(2234);
7635  return(RetStr);
7636  }
7637  if(AVI->Command == "jbo") //added at v2.15.0
7638  {
7639  TDateTime TTTime = TrainController->GetControllerTrainTime(28, AVI->EventTime, RptNum, IncrementalMinutes);
7640  if((DelayedRandMins >= 1) && !TimetableTime)
7641  {
7642  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7643  }
7644  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7645  {
7647  }
7648  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7649  {
7650  EventTime = Utilities->Format96HHMM(TTTime);
7651  }
7652  RetStr += "\nNew service joined by " + TrainController->GetRepeatHeadCode(68, AVI->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at approx. " + EventTime;
7653  Utilities->CallLogPop(2595); //above added GetRepeatHeadCode at v2.18.0 (was just AVI->OtherHeadCode before)
7654  return(RetStr);
7655  }
7656  if((AVI->Command == "Fns") || (AVI->Command == "F-nshs") || (AVI->Command == "Fns-sh")) //added at v2.15.0
7657  {
7658  TDateTime TTTime = TrainController->GetControllerTrainTime(29, AVI->EventTime, RptNum, IncrementalMinutes);
7659  if((DelayedRandMins >= 1) && !TimetableTime)
7660  {
7661  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7662  }
7663  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7664  {
7666  }
7667  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7668  {
7669  EventTime = Utilities->Format96HHMM(TTTime);
7670  }
7671  RetStr += "\nNew service finishes and forms another new service at approx. " + EventTime;
7672  Utilities->CallLogPop(2615);
7673  return(RetStr);
7674  }
7675  if(AVI->Command == "Fjo") //added at v2.15.0
7676  {
7677  TDateTime TTTime = TrainController->GetControllerTrainTime(26, AVI->EventTime, RptNum, IncrementalMinutes);
7678  if((DelayedRandMins >= 1) && !TimetableTime)
7679  {
7680  EventTime = Utilities->Format96HHMM(TTTime + TDateTime(DelayedRandMins/1440));
7681  }
7682  else if((TrainController->TTClockTime > TTTime) && !TimetableTime)
7683  {
7685  }
7686  else //((DelayedRandMins == 0) && (TTClockTime <= TTTime)) || TimetableTime
7687  {
7688  EventTime = Utilities->Format96HHMM(TTTime);
7689  }
7690  RetStr += "\nNew service finishes and joins " + TrainController->GetRepeatHeadCode(69, AVI->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at approx. " + EventTime;
7691  Utilities->CallLogPop(2605); //above added GetRepeatHeadCode at v2.18.0 (was just AVI->OtherHeadCode before)
7692  return(RetStr);
7693  }
7694  if(AVI->Command == "Frh") //added at v2.15.0
7695  {
7696  RetStr += "\nNew service finishes and remains at the location.";
7697  Utilities->CallLogPop(2606);
7698  return(RetStr);
7699  }
7700  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7701  {
7702  if(TimetableTime) //don't add random delay
7703  {
7704  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7705  if(CDTFlag)
7706  {
7707  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7708  {
7709  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
7710  }
7711  else
7712  {
7713  RetStr += "\nNew service changes direction then departs at " + DepTime;
7714  }
7715  }
7716  else
7717  {
7718  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7719  {
7720  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
7721  }
7722  else
7723  {
7724  RetStr += "\nNew service departs at " + DepTime;
7725  }
7726  }
7727  }
7728  else if(DelayedRandMins >= 1)//add random delay
7729  {
7730  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(24, AVI->DepartureTime + TDateTime(DelayedRandMins/1440), RptNum, IncrementalMinutes));
7731  if(CDTFlag)
7732  {
7733  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7734  {
7735  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
7736  }
7737  else
7738  {
7739  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
7740  }
7741  }
7742  else
7743  {
7744  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7745  {
7746  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
7747  }
7748  else
7749  {
7750  RetStr += "\nNew service departs at approx. " + DepTime;
7751  }
7752  }
7753  }
7754  else //no random delay but may be delayed for other reasons
7755  {
7756  TDateTime TTTime = TrainController->GetControllerTrainTime(25, AVI->DepartureTime, RptNum, IncrementalMinutes);
7757  if(TrainController->TTClockTime > TTTime)
7758  {
7760  }
7761  else
7762  {
7763  DepTime = Utilities->Format96HHMM(TTTime);
7764  }
7765  if(CDTFlag)
7766  {
7767  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7768  {
7769  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at approx. " + DepTime;
7770  }
7771  else
7772  {
7773  RetStr += "\nNew service changes direction then departs at approx. " + DepTime;
7774  }
7775  }
7776  else
7777  {
7778  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7779  {
7780  RetStr += "\nNew service departs towards " + TowardsLocation + " at approx. " + DepTime;
7781  }
7782  else
7783  {
7784  RetStr += "\nNew service departs at approx. " + DepTime;
7785  }
7786  }
7787  }
7788  Utilities->CallLogPop(2236);
7789  return(RetStr);
7790  }
7791  }
7792  Utilities->CallLogPop(2208);
7793  return(RetStr); //if reach here then RetStr doesn't change
7794 }
7795 
7796 // ---------------------------------------------------------------------------
7797 
7799 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7800 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
7801 {
7802  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7803  ",FloatingTimetableString" + "," + HeadCode);
7804  AnsiString RetStr = "", PartStr = "";
7805  int Count = 0;
7806  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
7807  AnsiString LocName = Ptr->LocationName;
7808 
7809  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7810  // can start in signaller control so exclude this
7811  {
7812  throw Exception("Error - start entry in FloatingTimetableString");
7813  }
7814  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
7815  bool FirstPass = true;
7816  Ptr--; // because incremented at start of loop
7817 
7818  // different first TimeTimeLoc display if in signaller control
7819  do
7820  {
7821  Ptr++;
7822  if((Ptr->FormatType == Repeat) || TimetableFinished)
7823  {
7824  break;
7825  }
7826  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7827  {
7828  AnsiString TrainLoc = "";
7829  if(TrainMode == Timetable)
7830  {
7831  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7832  {
7833  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7834  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7835  {
7836  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7837  }
7838  }
7839  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7840  {
7841  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7842  }
7843  else
7844  {
7845  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7846  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7847  Count++; // because there are 2 entries
7848  }
7849  }
7850  else // TrainMode == Signaller
7851  {
7852  if(DepartureTimeSet)
7853  {
7854  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7855  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7856  {
7857  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7858  }
7859  }
7860  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7861  {
7862  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7863  }
7864  else
7865  {
7866  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7867  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7868  Count++; // because there are 2 entries
7869  }
7870  }
7871  }
7872  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7873  {
7874  AnsiString TrainLoc = "";
7875  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7876  {
7877  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7878  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7879  {
7880  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7881  }
7882  }
7883  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7884  {
7885  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7886  }
7887  else
7888  {
7889  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7890  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7891  Count++; // because there are 2 entries
7892  }
7893  }
7894  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7895  {
7896  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7897  }
7898  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7899  {
7900  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7901  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7902  {
7903  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7904  }
7905  }
7906  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewService)
7907  { //note that TreatPassAsTimeLocDeparture can't be set if have SkippedDeparture
7908  PartStr = Utilities->Format96HHMM(GetTrainTime(47, Ptr->EventTime)) + ": Depart from " + Ptr->LocationName;
7909  }
7910  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7911  {
7912  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7913  }
7914  else if(Ptr->Command == "Fns")
7915  {
7916  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7917  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7918  PartStr = GetNewServiceDepartureInfo(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to PartStr
7919  }
7920  else if(Ptr->Command == "F-nshs")
7921  {
7922  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7923  Ptr->LocationName;
7924  PartStr = GetNewServiceDepartureInfo(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7925  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7926  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7927  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7928  }
7929  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7930  {
7931  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7932  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7933  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7934  PartStr = GetNewServiceDepartureInfo(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7935  }
7936  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7937  {
7938  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7939  +" at " + Ptr->LocationName;
7940  PartStr = GetNewServiceDepartureInfo(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7941  }
7942  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7943  {
7944  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7945  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7946  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7947  PartStr = GetNewServiceDepartureInfo(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr, true); //if there is a next service this adds the new service departure time to RetStr
7948  }
7949  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7950  {
7951  PartStr = "Terminate at " + Ptr->LocationName;
7952  }
7953  else if(Ptr->Command == "Frh")
7954  {
7955  PartStr = "Terminate at " + Ptr->LocationName;
7956  }
7957  else if(Ptr->Command == "Fer")
7958  {
7959  AnsiString AllowedExits = "";
7960  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7961  }
7962  else if(Ptr->Command == "Fjo")
7963  {
7964  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7965  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7966  }
7967  else if(Ptr->Command == "jbo")
7968  {
7969  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7970  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7971  }
7972  else if(Ptr->Command == "fsp")
7973  {
7974  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7975  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7976  if(Ptr->SplitDistribution != "")
7977  {
7978  PartStr+= ", split mass%-Power% = " + Ptr->SplitDistribution;
7979  }
7980  else
7981  {
7982  PartStr+= ", split mass%-Power% = 50-50";
7983  }
7984  }
7985  else if(Ptr->Command == "rsp")
7986  {
7987  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7988  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7989  if(Ptr->SplitDistribution != "")
7990  {
7991  PartStr+= ", split mass%-Power% = " + Ptr->SplitDistribution;
7992  }
7993  else
7994  {
7995  PartStr+= ", split mass%-Power% = 50-50";
7996  }
7997  }
7998  else if(Ptr->Command == "cdt")
7999  {
8000  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
8001  }
8002  else if(Ptr->Command == "dsc")
8003  {
8004  PartStr = Utilities->Format96HHMM(GetTrainTime(67, Ptr->EventTime)) + ": Change description at " + Ptr->LocationName;
8005  }
8006  if(RetStr != "")
8007  {
8008  RetStr = RetStr + '\n' + PartStr;
8009  }
8010  else
8011  {
8012  RetStr = PartStr;
8013  }
8014  FirstPass = false;
8015  Count++;
8016 
8017  if(SkipDep)
8018  {
8019  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
8020  Ptr--; //it is incremented at the start of the next loop
8021  SkipDep = false;
8022  SkipDepActedOn = true;
8023  }
8024  }
8025  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
8026  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
8027  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
8028  // forward as anyone should wish to see without looking at the full timetable
8029  if(TimetableFinished)
8030  {
8031  if(TrainMode == Timetable)
8032  {
8033  RetStr = "Timetable finished";
8034  }
8035  else
8036  {
8037  RetStr = "No timetable";
8038  }
8039  }
8040  Utilities->CallLogPop(1125);
8041  return("Timetable:\n" + RetStr);
8042 }
8043 
8044 // ---------------------------------------------------------------------------
8045 
8046 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
8047 {
8048  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
8049  Utilities->SaveFileString(OutFile, HeadCode);
8052  Utilities->SaveFileInt(OutFile, StartSpeed);
8055  Utilities->SaveFileInt(OutFile, RepeatNumber);
8058  Utilities->SaveFileInt(OutFile, Mass);
8061  Utilities->SaveFileDouble(OutFile, EntrySpeed);
8068  Utilities->SaveFileDouble(OutFile, BrakeRate);
8072  Utilities->SaveFileDouble(OutFile, double(EntryTime));
8073  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
8074  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
8075  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
8076  Utilities->SaveFileDouble(OutFile, double(TRSTime));
8077  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
8081  Utilities->SaveFileInt(OutFile, (short)TrainMode);
8086  Utilities->SaveFileBool(OutFile, Derailed);
8088  Utilities->SaveFileBool(OutFile, Crashed);
8095  Utilities->SaveFileBool(OutFile, NotInService);
8096  Utilities->SaveFileBool(OutFile, Plotted);
8097  Utilities->SaveFileBool(OutFile, TrainGone);
8098  Utilities->SaveFileBool(OutFile, SPADFlag);
8100  Utilities->SaveFileInt(OutFile, HOffset[0]);
8101  Utilities->SaveFileInt(OutFile, HOffset[1]);
8102  Utilities->SaveFileInt(OutFile, HOffset[2]);
8103  Utilities->SaveFileInt(OutFile, HOffset[3]);
8104  Utilities->SaveFileInt(OutFile, VOffset[0]);
8105  Utilities->SaveFileInt(OutFile, VOffset[1]);
8106  Utilities->SaveFileInt(OutFile, VOffset[2]);
8107  Utilities->SaveFileInt(OutFile, VOffset[3]);
8108  Utilities->SaveFileInt(OutFile, PlotElement[0]);
8109  Utilities->SaveFileInt(OutFile, PlotElement[1]);
8110  Utilities->SaveFileInt(OutFile, PlotElement[2]);
8111  Utilities->SaveFileInt(OutFile, PlotElement[3]);
8112  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
8113  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
8114  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
8115  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
8117  Utilities->SaveFileInt(OutFile, (short)Straddle);
8118  Utilities->SaveFileInt(OutFile, NextTrainID);
8119  Utilities->SaveFileInt(OutFile, TrainID);
8120  Utilities->SaveFileInt(OutFile, LeadElement);
8121  Utilities->SaveFileInt(OutFile, LeadEntryPos);
8122  Utilities->SaveFileInt(OutFile, LeadExitPos);
8123  Utilities->SaveFileInt(OutFile, MidElement);
8124  Utilities->SaveFileInt(OutFile, MidEntryPos);
8125  Utilities->SaveFileInt(OutFile, MidExitPos);
8126  Utilities->SaveFileInt(OutFile, LagElement);
8127  Utilities->SaveFileInt(OutFile, LagEntryPos);
8128  Utilities->SaveFileInt(OutFile, LagExitPos);
8129  int ColourNumber;
8130 
8132  {
8133  ColourNumber = 0;
8134  }
8136  {
8137  ColourNumber = 1;
8138  }
8140  {
8141  ColourNumber = 2;
8142  }
8144  {
8145  ColourNumber = 3;
8146  }
8148  {
8149  ColourNumber = 4;
8150  }
8152  {
8153  ColourNumber = 5;
8154  }
8156  {
8157  ColourNumber = 6;
8158  }
8160  {
8161  ColourNumber = 7;
8162  }
8164  {
8165  ColourNumber = 8;
8166  }
8168  {
8169  ColourNumber = 9;
8170  }
8172  {
8173  ColourNumber = 10;
8174  }
8176  {
8177  ColourNumber = 11;
8178  }
8180  {
8181  ColourNumber = 12;
8182  }
8183  else if(BackgroundColour == clTRSBackground)
8184  {
8185  ColourNumber = 13;
8186  }
8188  {
8189  ColourNumber = 14; // added at v2.4.0
8190  }
8191  Utilities->SaveFileInt(OutFile, ColourNumber);
8192 
8193  // additional data
8194  bool ForwardHeadCode;
8195 
8196  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
8197  {
8198  ForwardHeadCode = true;
8199  }
8200  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
8201  else
8202  {
8203  ForwardHeadCode = false;
8204  }
8205  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
8206 
8207  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
8208 
8209  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
8210  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
8211 
8212  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
8213  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
8214  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
8215  // so use the last asterisk position for this - 0 for false & 1 for true
8216  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
8217  AnsiString Marker;
8218 
8220  {
8221  Marker = "*****1";
8222  }
8223  else
8224  {
8225  Marker = "*****0";
8226  }
8227  if(RestoreTimetableLocation == "")
8228  {
8229  Utilities->SaveFileString(OutFile, Marker);
8230  }
8231  else
8232  {
8233  AnsiString CombinedString = RestoreTimetableLocation + Marker;
8234  Utilities->SaveFileString(OutFile, CombinedString);
8235  // RestoreTimetableLocation + marker
8236  }
8237  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
8238  Utilities->CallLogPop(1457);
8239 }
8240 
8241 // ---------------------------------------------------------------------------
8242 
8243 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
8244 {
8245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
8246  HeadCode = Utilities->LoadFileString(InFile);
8249  StartSpeed = Utilities->LoadFileInt(InFile);
8251  if(SignallerMaxSpeed < 10)
8252  {
8253  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
8254  }
8256  RepeatNumber = Utilities->LoadFileInt(InFile);
8259  Mass = Utilities->LoadFileInt(InFile);
8262  {
8264  }
8265  // above added at v2.1.0 for legacy session files where value may not have been limited
8267  EntrySpeed = Utilities->LoadFileDouble(InFile);
8271  if(TimetableMaxRunningSpeed < 10)
8272  {
8273  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8274  }
8276  if(MaxRunningSpeed < 10)
8277  {
8278  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
8279  }
8282  BrakeRate = Utilities->LoadFileDouble(InFile);
8286  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
8287  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
8288  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
8289  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
8290  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
8291  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
8300  Derailed = Utilities->LoadFileBool(InFile);
8302  Crashed = Utilities->LoadFileBool(InFile);
8309  NotInService = Utilities->LoadFileBool(InFile);
8310  Plotted = Utilities->LoadFileBool(InFile);
8311  TrainGone = Utilities->LoadFileBool(InFile);
8312  SPADFlag = Utilities->LoadFileBool(InFile);
8314  HOffset[0] = Utilities->LoadFileInt(InFile);
8315  HOffset[1] = Utilities->LoadFileInt(InFile);
8316  HOffset[2] = Utilities->LoadFileInt(InFile);
8317  HOffset[3] = Utilities->LoadFileInt(InFile);
8318  VOffset[0] = Utilities->LoadFileInt(InFile);
8319  VOffset[1] = Utilities->LoadFileInt(InFile);
8320  VOffset[2] = Utilities->LoadFileInt(InFile);
8321  VOffset[3] = Utilities->LoadFileInt(InFile);
8322  PlotElement[0] = Utilities->LoadFileInt(InFile);
8323  PlotElement[1] = Utilities->LoadFileInt(InFile);
8324  PlotElement[2] = Utilities->LoadFileInt(InFile);
8325  PlotElement[3] = Utilities->LoadFileInt(InFile);
8326  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
8327  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
8328  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
8329  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
8331  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
8332  NextTrainID = Utilities->LoadFileInt(InFile);
8333  // will be same for all but best to save all anyway
8334  TrainID = Utilities->LoadFileInt(InFile);
8335  LeadElement = Utilities->LoadFileInt(InFile);
8336  LeadEntryPos = Utilities->LoadFileInt(InFile);
8337  LeadExitPos = Utilities->LoadFileInt(InFile);
8338  MidElement = Utilities->LoadFileInt(InFile);
8339  MidEntryPos = Utilities->LoadFileInt(InFile);
8340  MidExitPos = Utilities->LoadFileInt(InFile);
8341  LagElement = Utilities->LoadFileInt(InFile);
8342  LagEntryPos = Utilities->LoadFileInt(InFile);
8343  LagExitPos = Utilities->LoadFileInt(InFile);
8344  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
8345 
8346  if(ColourNumber == 0)
8347  {
8349  }
8350  else if(ColourNumber == 1)
8351  {
8353  }
8354  else if(ColourNumber == 2)
8355  {
8357  }
8358  else if(ColourNumber == 3)
8359  {
8361  }
8362  else if(ColourNumber == 4)
8363  {
8365  }
8366  else if(ColourNumber == 5)
8367  {
8369  }
8370  else if(ColourNumber == 6)
8371  {
8373  }
8374  else if(ColourNumber == 7)
8375  {
8377  }
8378  else if(ColourNumber == 8)
8379  {
8381  }
8382  else if(ColourNumber == 9)
8383  {
8385  }
8386  else if(ColourNumber == 10)
8387  {
8389  }
8390  else if(ColourNumber == 11)
8391  {
8393  }
8394  else if(ColourNumber == 12)
8395  {
8397  }
8398  else if(ColourNumber == 13)
8399  {
8401  }
8402  else if(ColourNumber == 14)
8403  {
8404  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
8405 
8406  }
8407  // additional data
8409  // sets the BackgroundColour to the loaded value
8410  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
8411 
8412  if(ForwardHeadCode)
8413  {
8414  for(int x = 0; x < 4; x++)
8415  {
8417  }
8418  }
8419  else
8420  {
8421  for(int x = 0; x < 4; x++)
8422  {
8423  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
8424  }
8425  }
8426  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
8427  if(TrainMode == Timetable)
8428  {
8429  if(Crashed)
8430  {
8432  }
8433  else
8434  {
8436  }
8437  }
8438  else
8439  {
8441  }
8443  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
8444  if(Straddle == LeadMid)
8445  {
8446  if(LeadElement > -1)
8447  {
8449  }
8450  if(LeadElement > -1)
8451  {
8453  }
8454  if(MidElement > -1)
8455  {
8457  }
8458  if(MidElement > -1)
8459  {
8461  }
8462  }
8463  else if(Straddle == LeadMidLag)
8464  {
8465  if(LeadElement > -1)
8466  {
8468  }
8469  if(MidElement > -1)
8470  {
8472  }
8473  if(MidElement > -1)
8474  {
8476  }
8477  if(LagElement > -1)
8478  {
8480  }
8481  }
8482  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
8483 
8484  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
8485  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
8486 
8487  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
8488 
8489  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
8490  if(LeadElement > -1)
8491  // need to include this in case train exiting & no lead element
8492  {
8494  {
8495  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
8496  }
8497  }
8498  AValue = sqrt(2 * PowerAtRail / Mass);
8499 
8500  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
8501 
8502  // possible RestoreTimetableLocation + Marker, where Marker is
8503  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
8504  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
8505  // added at beta v0.2e
8506  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
8507  // name not allowed to include the '*' character
8508  {
8509  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
8510  bool GiveMessagesFalse = false;
8511  bool CheckLocationsExistInRailwayTrue = true;
8512  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
8513  {
8514  // otherwise take no action
8515  RestoreTimetableLocation = Location;
8516  }
8517  }
8518  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
8519 
8520  StoppedWithoutPower = false;
8521  if(Marker[6] == '1')
8522  {
8523  StoppedWithoutPower = true;
8524  }
8525  Utilities->CallLogPop(1458);
8526 }
8527 
8528 // ---------------------------------------------------------------------------
8529 
8530 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
8531 {
8533  {
8534  return(false); // HeadCode
8535 
8536  }
8537  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8538  {
8539  return(false); // RearStartElement
8540 
8541  }
8542  if(!Utilities->CheckFileInt(InFile, 0, 3))
8543  {
8544  return(false); // RearStartExitPos
8545 
8546  }
8547  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8548  {
8549  return(false); // StartSpeed
8550 
8551  }
8552  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
8553  {
8554  return(false); // SignallerMaxSpeed
8555 
8556  }
8557  if(!Utilities->CheckFileBool(InFile))
8558  {
8559  return(false); // HoldAtLocationInTTMode
8560 
8561  }
8562  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8563  {
8564  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
8565 
8566  }
8567  if(!Utilities->CheckFileInt(InFile, 0, 5760))
8568  {
8569  return(false); // IncrementalMinutes (max 96 x 60)
8570 
8571  }
8572  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8573  {
8574  return(false); // IncrementalDigits
8575 
8576  }
8577  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8578  {
8579  return(false); // Mass
8580 
8581  }
8582  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
8583  {
8584  return(false);
8585  }
8586  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
8587  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
8588  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
8589  {
8590  return(false); // FrontElementLength
8591 
8592  }
8593  if(!Utilities->CheckFileDouble(InFile))
8594  {
8595  return(false); // EntrySpeed
8596 
8597  }
8598  if(!Utilities->CheckFileDouble(InFile))
8599  {
8600  return(false); // ExitSpeedHalf
8601 
8602  }
8603  if(!Utilities->CheckFileDouble(InFile))
8604  {
8605  return(false); // ExitSpeedFull
8606 
8607  }
8608  if(!Utilities->CheckFileDouble(InFile))
8609  {
8610  return(false); // TimetableMaxRunningSpeed
8611 
8612  }
8613  if(!Utilities->CheckFileDouble(InFile))
8614  {
8615  return(false); // MaxRunningSpeed
8616 
8617  }
8618  if(!Utilities->CheckFileDouble(InFile))
8619  {
8620  return(false); // MaxExitSpeed
8621 
8622  }
8623  if(!Utilities->CheckFileDouble(InFile))
8624  {
8625  return(false); // MaxBrakeRate
8626 
8627  }
8628  if(!Utilities->CheckFileDouble(InFile))
8629  {
8630  return(false); // BrakeRate
8631 
8632  }
8633  if(!Utilities->CheckFileDouble(InFile))
8634  {
8635  return(false); // PowerAtRail
8636 
8637  }
8638  if(!Utilities->CheckFileBool(InFile))
8639  {
8640  return(false); // FirstHalfMove
8641 
8642  }
8643  if(!Utilities->CheckFileBool(InFile))
8644  {
8645  return(false); // OneLengthAccelDecel
8646 
8647  }
8648  if(!Utilities->CheckFileDouble(InFile))
8649  {
8650  return(false); // double(EntryTime)
8651 
8652  }
8653  if(!Utilities->CheckFileDouble(InFile))
8654  {
8655  return(false); // double(ExitTimeHalf)
8656 
8657  }
8658  if(!Utilities->CheckFileDouble(InFile))
8659  {
8660  return(false); // double(ExitTimeFull)
8661 
8662  }
8663  if(!Utilities->CheckFileDouble(InFile))
8664  {
8665  return(false); // double(ReleaseTime)
8666 
8667  }
8668  if(!Utilities->CheckFileDouble(InFile))
8669  {
8670  return(false); // double(TRSTime)
8671 
8672  }
8673  if(!Utilities->CheckFileDouble(InFile))
8674  {
8675  return(false); // double(LastActionTime)
8676 
8677  }
8678  if(!Utilities->CheckFileBool(InFile))
8679  {
8680  return(false); // CallingOnFlag
8681 
8682  }
8683  if(!Utilities->CheckFileBool(InFile))
8684  {
8685  return(false); // BeingCalledOn
8686 
8687  }
8688  if(!Utilities->CheckFileBool(InFile))
8689  {
8690  return(false); // DepartureTimeSet
8691 
8692  }
8693  if(!Utilities->CheckFileInt(InFile, 0, 2))
8694  {
8695  return(false); // (short)TrainMode
8696 
8697  }
8698  if(!Utilities->CheckFileBool(InFile))
8699  {
8700  return(false); // TimetableFinished
8701 
8702  }
8703  if(!Utilities->CheckFileBool(InFile))
8704  {
8705  return(false); // LastActionDelayFlag
8706 
8707  }
8708  if(!Utilities->CheckFileBool(InFile))
8709  {
8710  return(false); // SignallerRemoved
8711 
8712  }
8713  if(!Utilities->CheckFileBool(InFile))
8714  {
8715  return(false); // TerminatedMessageSent
8716 
8717  }
8718  if(!Utilities->CheckFileBool(InFile))
8719  {
8720  return(false); // Derailed
8721 
8722  }
8723  if(!Utilities->CheckFileBool(InFile))
8724  {
8725  return(false); // DerailPending
8726 
8727  }
8728  if(!Utilities->CheckFileBool(InFile))
8729  {
8730  return(false); // Crashed
8731 
8732  }
8733  if(!Utilities->CheckFileBool(InFile))
8734  {
8735  return(false); // StoppedAtBuffers
8736 
8737  }
8738  if(!Utilities->CheckFileBool(InFile))
8739  {
8740  return(false); // StoppedAtSignal
8741 
8742  }
8743  if(!Utilities->CheckFileBool(InFile))
8744  {
8745  return(false); // StoppedAtLocation
8746 
8747  }
8748  if(!Utilities->CheckFileBool(InFile))
8749  {
8750  return(false); // SignallerStopped
8751 
8752  }
8753  if(!Utilities->CheckFileBool(InFile))
8754  {
8755  return(false); // StoppedAfterSPAD
8756 
8757  }
8758  if(!Utilities->CheckFileBool(InFile))
8759  {
8760  return(false); // StoppedForTrainInFront
8761 
8762  }
8763  if(!Utilities->CheckFileBool(InFile))
8764  {
8765  return(false); // NotInService
8766 
8767  }
8768  if(!Utilities->CheckFileBool(InFile))
8769  {
8770  return(false); // Plotted
8771 
8772  }
8773  if(!Utilities->CheckFileBool(InFile))
8774  {
8775  return(false); // TrainGone
8776 
8777  }
8778  if(!Utilities->CheckFileBool(InFile))
8779  {
8780  return(false); // SPADFlag
8781 
8782  }
8783  if(!Utilities->CheckFileBool(InFile))
8784  {
8785  return(false); // TimeTimeLocArrived
8786 
8787  }
8788  if(!Utilities->CheckFileInt(InFile, 0, 15))
8789  {
8790  return(false); // HOffset[0]
8791 
8792  }
8793  if(!Utilities->CheckFileInt(InFile, 0, 15))
8794  {
8795  return(false); // HOffset[1]
8796 
8797  }
8798  if(!Utilities->CheckFileInt(InFile, 0, 15))
8799  {
8800  return(false); // HOffset[2]
8801 
8802  }
8803  if(!Utilities->CheckFileInt(InFile, 0, 15))
8804  {
8805  return(false); // HOffset[3]
8806 
8807  }
8808  if(!Utilities->CheckFileInt(InFile, 0, 15))
8809  {
8810  return(false); // VOffset[0]
8811 
8812  }
8813  if(!Utilities->CheckFileInt(InFile, 0, 15))
8814  {
8815  return(false); // VOffset[1]
8816 
8817  }
8818  if(!Utilities->CheckFileInt(InFile, 0, 15))
8819  {
8820  return(false); // VOffset[2]
8821 
8822  }
8823  if(!Utilities->CheckFileInt(InFile, 0, 15))
8824  {
8825  return(false); // VOffset[3]
8826 
8827  }
8828  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8829  {
8830  return(false); // PlotElement[0]
8831 
8832  }
8833  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8834  {
8835  return(false); // PlotElement[1]
8836 
8837  }
8838  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8839  {
8840  return(false); // PlotElement[2]
8841 
8842  }
8843  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8844  {
8845  return(false); // PlotElement[3]
8846 
8847  }
8848  if(!Utilities->CheckFileInt(InFile, 0, 3))
8849  {
8850  return(false); // PlotEntryPos[0]
8851 
8852  }
8853  if(!Utilities->CheckFileInt(InFile, 0, 3))
8854  {
8855  return(false); // PlotEntryPos[1]
8856 
8857  }
8858  if(!Utilities->CheckFileInt(InFile, 0, 3))
8859  {
8860  return(false); // PlotEntryPos[2]
8861 
8862  }
8863  if(!Utilities->CheckFileInt(InFile, 0, 3))
8864  {
8865  return(false); // PlotEntryPos[3]
8866 
8867  }
8868  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8869  {
8870  return(false); // TrainCrashedInto
8871 
8872  }
8873  if(!Utilities->CheckFileInt(InFile, 0, 2))
8874  {
8875  return(false); // (short)Straddle
8876 
8877  }
8878  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8879  {
8880  return(false); // NextTrainID
8881 
8882  }
8883  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8884  {
8885  return(false); // TrainID
8886 
8887  }
8888  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8889  {
8890  return(false); // LeadElement
8891 
8892  }
8893  if(!Utilities->CheckFileInt(InFile, 0, 3))
8894  {
8895  return(false); // LeadEntryPos
8896 
8897  }
8898  if(!Utilities->CheckFileInt(InFile, 0, 3))
8899  {
8900  return(false); // LeadExitPos
8901 
8902  }
8903  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8904  {
8905  return(false); // MidElement
8906 
8907  }
8908  if(!Utilities->CheckFileInt(InFile, 0, 3))
8909  {
8910  return(false); // MidEntryPos
8911 
8912  }
8913  if(!Utilities->CheckFileInt(InFile, 0, 3))
8914  {
8915  return(false); // MidExitPos
8916 
8917  }
8918  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8919  {
8920  return(false); // LagElement
8921 
8922  }
8923  if(!Utilities->CheckFileInt(InFile, 0, 3))
8924  {
8925  return(false); // LagEntryPos
8926 
8927  }
8928  if(!Utilities->CheckFileInt(InFile, 0, 3))
8929  {
8930  return(false); // LagExitPos
8931 
8932  }
8933  if(!Utilities->CheckFileInt(InFile, 0, 14))
8934  {
8935  return(false);
8936  }
8937  // Background colour number //14 is failed colour at v2.4.0
8938  if(!Utilities->CheckFileBool(InFile))
8939  {
8940  return(false); // ForwardHeadCode
8941 
8942  }
8943  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8944  {
8945  return(false); // TrainDataEntryValue
8946 
8947  }
8948  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8949  {
8950  return(false); // ActionVectorEntryValue
8951 
8952  }
8954  {
8955  return(false); // End of train marker + possible RestoreTimetableLocation
8956 
8957  }
8958  // and StoppedWithoutPower flag
8959  return(true);
8960 }
8961 
8962 // ---------------------------------------------------------------------------
8963 
8964 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8965 {
8966  // order below reflects significance so earlier shows first, as may have more than one flag set
8967  // only plot flashing trains when Flash is true
8968 
8969 /*
8970  clCrashedBackground (TColor)0x0000FF red
8971  clDerailedBackground (TColor)0x0000FF red
8972  clSPADBackground (TColor)0x00FFFF yellow
8973  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8974  clCallOnBackground (TColor)0xFF33FF light magenta
8975  clSignalStopBackground (TColor)0x00FF66 green
8976  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8977  clStationStopBackground (TColor)0xCCFFCC pale green
8978  clTRSBackground (TColor)0xFFCCFF light pink
8979  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8980  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8981  clSignallerStopped (TColor)0x99CCFF caramel
8982  clNormalBackground (TColor)0xCCCCCC grey
8983 */
8984 
8985  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8986  bool HideFlashingTrain = true;
8987  // hide it when Flash false so it blinks on and off
8988  // if don't hide it it stays displayed all the time
8989  Graphics::TBitmap *SmallTrainBitmap;
8990 
8991  // NB ensure retain same order as zoomed in order so colours correspond
8993  {
8994  TrainController->CrashWarning = true;
8995  SmallTrainBitmap = RailGraphics->smRed;
8996  }
8998  {
9000  SmallTrainBitmap = RailGraphics->smRed;
9001  }
9003  {
9004  TrainController->SPADWarning = true;
9005  SmallTrainBitmap = RailGraphics->smYellow;
9006  }
9008  {
9010  SmallTrainBitmap = RailGraphics->smOrange;
9011  }
9013  {
9015  SmallTrainBitmap = RailGraphics->smMagenta;
9016  }
9018  {
9020  SmallTrainBitmap = RailGraphics->smBrightGreen;
9021  }
9023  {
9025  SmallTrainBitmap = RailGraphics->smCyan;
9026  }
9028  {
9029  SmallTrainBitmap = RailGraphics->smPaleGreen;
9030  HideFlashingTrain = false;
9031  }
9033  {
9034  SmallTrainBitmap = RailGraphics->smCyan;
9035  HideFlashingTrain = false;
9036  }
9038  {
9039  SmallTrainBitmap = RailGraphics->smLightBlue;
9040  HideFlashingTrain = false;
9041  }
9043  {
9044  SmallTrainBitmap = RailGraphics->smCaramel;
9045  HideFlashingTrain = false;
9046  }
9047  else
9048  {
9049  SmallTrainBitmap = RailGraphics->smBlack; // moving
9050  HideFlashingTrain = false;
9051  }
9052  // now plot the new train
9053  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
9054  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
9055  {
9056  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
9057  }
9058  if((MidElement > -1) && (!HideFlashingTrain || Flash))
9059  {
9060  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
9061  }
9062  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
9063  {
9064  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
9065  }
9069  Utilities->CallLogPop(1459);
9070 }
9071 
9072 // ---------------------------------------------------------------------------
9073 
9075 {
9076  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
9077  if(!Display->ZoomOutFlag)
9078  {
9079  Utilities->CallLogPop(1304);
9080  return;
9081  }
9082  for(int y = 0; y < 3; y++)
9083  {
9084  if(OldZoomOutElement[y] > -1)
9085  {
9086  bool FoundFlag = false;
9087  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
9088  TTrackElement IATElement1, IATElement2;
9089  // default elements to begin with
9090  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
9091  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
9092  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
9093  if(FoundFlag)
9094  {
9095  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
9096  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
9097  if(IMPair.first != IMPair.second)
9098  {
9099  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
9100  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
9101  }
9102  }
9103  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
9104  }
9105  }
9106  Utilities->CallLogPop(1305);
9107 }
9108 
9109 // ---------------------------------------------------------------------------
9110 
9111 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
9112 {
9113  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
9114  LocationName = "";
9115  if(!RevisedStoppedAtLoc())
9116  {
9117  Utilities->CallLogPop(1398);
9118  return(false);
9119  }
9120  if(LeadElement > -1)
9121  {
9123  }
9124  if((LocationName == "") && (MidElement > -1))
9125  {
9126  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
9127  }
9128  if((LocationName == "") && (LagElement > -1))
9129  {
9130  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
9131  }
9132  if(LocationName == "")
9133  {
9134  throw Exception("Error - Location name not set in TrainAtLocation");
9135  }
9136  Utilities->CallLogPop(1399);
9137  return(true);
9138 }
9139 
9140 // ---------------------------------------------------------------------------
9141 
9142 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
9143 {
9144  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
9145  for(int x = 0; x < 4; x++)
9146  {
9147  PlotTrainGraphic(7, x, Disp);
9148  }
9149  Utilities->CallLogPop(647);
9150 }
9151 
9152 // ---------------------------------------------------------------------------
9153 
9154 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
9155 {
9156  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
9157  for(int x = 0; x < 4; x++)
9158  {
9159  if(PlotElement[x] > -1)
9160  {
9161  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
9162  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
9163  }
9164  }
9165  Utilities->CallLogPop(1708);
9166 }
9167 
9168 // ---------------------------------------------------------------------------
9169 
9170 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
9171 {
9172  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
9173  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
9174  AnsiString(LinkNumber) + "," + HeadCode);
9175 
9176 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
9177  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
9178  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
9179 */
9180 
9181  // note that MidElement always fully occupied
9182  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
9183  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
9184  {
9185  Utilities->CallLogPop(2005);
9186  return(true);
9187  }
9188  if(Straddle == LeadMid)
9189  {
9190  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
9191  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
9192  {
9193  Utilities->CallLogPop(2006);
9194  return(true);
9195  }
9196  }
9197  else if(Straddle == LeadMidLag)
9198  {
9199  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
9200  // only interested in LeadEntryPos as train not occupying ExitPos yet
9201  {
9202  Utilities->CallLogPop(2007);
9203  return(true);
9204  }
9205  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
9206  // only interested in LagExitPos as train has left EntryPos
9207  {
9208  Utilities->CallLogPop(2008);
9209  return(true);
9210  }
9211  }
9212  Utilities->CallLogPop(2009);
9213  return(false);
9214 }
9215 
9216 // ---------------------------------------------------------------------------
9217 
9218 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
9223 // TimeToExit & ExitPair added for multiplayer
9224 {
9225  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
9226  int DistanceToRedSignal = 0, DistanceToExit = -1;
9227  float TimeToAct = 0, LastTimeToExit = TimeToExit;
9228  TimeToExit = -1;
9229  ExitPair.first = -1;
9230  ExitPair.second = -1;
9231  float MinsEarly = 0; //added at v2.6.1
9232  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
9233  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
9234  float TempTTE;
9235 
9236  if(TrainFailed && Stopped() && (TrainMode != Signaller))
9237  {
9238  Utilities->CallLogPop(2147);
9239  return(0); // time to act now, time to exit set above
9240  }
9241  if(SignallerStopped)
9242  {
9243  Utilities->CallLogPop(2080);
9244  return(-1); //time to exit set above
9245  }
9246  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
9247  {
9248  Utilities->CallLogPop(2266);
9249  return(-1); // time to exit set above
9250  }
9251 
9252  // check if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
9253  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
9254  /*&& (ExitSpeedFull > 1)*/) //LagElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9255  {
9256  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
9257  {
9258  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9259  {
9260  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9261  if(TempTTE < LastTimeToExit)
9262  {
9263  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9264  }
9265  else
9266  {
9267  TimeToExit = LastTimeToExit;
9268  }
9269  }
9270  else
9271  {
9272  TimeToExit = LastTimeToExit;
9273  }
9274  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
9275  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
9276  Utilities->CallLogPop(2342);
9277  return(-1);
9278  }
9279  else
9280  {
9281  TimeToExit = 0; //all train exited
9282  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
9283  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
9284  Utilities->CallLogPop(2343);
9285  return(-1);
9286  }
9287  }
9288  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation)/* && (ExitSpeedFull > 1)*/)
9289  //here MidElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9290  {
9291  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
9292  {
9293  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9294  {
9295  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9296  if(TempTTE < LastTimeToExit)
9297  {
9298  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9299  }
9300  else
9301  {
9302  TimeToExit = LastTimeToExit;
9303  }
9304  }
9305  else
9306  {
9307  TimeToExit = LastTimeToExit;
9308  }
9309  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
9310  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
9311  Utilities->CallLogPop(2344);
9312  return(-1);
9313  }
9314  else //front element of train fully off the exit, one length to exit
9315  {
9316  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9317  {
9318  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9319  if(TempTTE < LastTimeToExit)
9320  {
9321  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9322  }
9323  else
9324  {
9325  TimeToExit = LastTimeToExit;
9326  }
9327  }
9328  else
9329  {
9330  TimeToExit = LastTimeToExit;
9331  }
9332  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
9333  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
9334  Utilities->CallLogPop(2345);
9335  return(-1);
9336  }
9337  }
9338  if(LeadElement > -1)
9339  {
9341  /* && (ExitSpeedFull > 1)*/) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
9342  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
9343  //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
9344  if(Straddle == LeadMidLag)
9345  {
9346  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9347  {
9348  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9349  if(TempTTE < LastTimeToExit)
9350  {
9351  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9352  }
9353  else
9354  {
9355  TimeToExit = LastTimeToExit;
9356  }
9357  }
9358  else
9359  {
9360  TimeToExit = LastTimeToExit;
9361  }
9362  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
9363  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
9364  Utilities->CallLogPop(2346);
9365  return(-1);
9366  }
9367  else
9368  {
9369  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
9370  {
9371  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
9372  if(TempTTE < LastTimeToExit)
9373  {
9374  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
9375  }
9376  else
9377  {
9378  TimeToExit = LastTimeToExit;
9379  }
9380  }
9381  else
9382  {
9383  TimeToExit = LastTimeToExit;
9384  }
9385  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
9386  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
9387  Utilities->CallLogPop(2347);
9388  return(-1);
9389  }
9390  }
9391  }
9392 //here if LeadElement > -1 and its forward connection also > -1
9393 
9394  // calc distance to next red signal
9395  if(!Stopped() || RevisedStoppedAtLoc())
9396  {
9397  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
9398  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
9399  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
9400 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
9401  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
9402  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
9403  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
9404  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
9405  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
9406  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
9407  before the train has stopped the current station is still recognised as a future stop.
9408  In signaller mode stops don't count, and if pass stop signal command is given then when have LeadMidLag the current element
9409  becomes the signal, and the time to act indication becomes 'NOW'.
9410 */
9411  {
9412  FirstPosToBeMeasured = LeadElement;
9413  FirstEntryPos = LeadEntryPos;
9414  }
9415  float CurrentStopTime; // set to 0 at start of function
9416  float LaterStopTime; // set to 0 at start of function
9417  float RecoverableTime; // set to 0 at start of function
9418  int AvTrackSpeed; // set to zero at start of function
9419  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
9420  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
9421  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
9422 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
9423 //Therefore since need to calculate the time for each in the same way use a generic
9424 
9425  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
9426  {
9427  TimeToExit = -1;
9428  Utilities->CallLogPop(2076);
9429  return(-1);
9430  }
9431 //else one or other is set
9432  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
9433  bool DistanceToExitSet = (DistanceToExit > -1);
9434  int GenericDistance = DistanceToRedSignal;
9435  if(DistanceToExitSet)
9436  {
9437  GenericDistance = DistanceToExit;
9438  }
9439 /* Have MinsDelayed; pos or neg,
9440  CurrentStopTime; pos or zero
9441  LaterStopTime; pos or zero
9442  RecoverableTime; pos or zero
9443 
9444  & from these calculate TotalStopTime. noting that:
9445  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
9446  RecoverableTime always < LaterStopTime or both zero
9447  can't subtract more than RecoverableTime (MinsDelayed > 0)
9448  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
9449  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
9450  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
9451  if running early & stopped at location CurrentStopTime will automatically include the excess
9452 */
9453  float TimeToSubtract, TotalStopTime;
9454  if(MinsDelayed > RecoverableTime)
9455  {
9456  TimeToSubtract = RecoverableTime;
9457  }
9458  else
9459  {
9460  TimeToSubtract = MinsDelayed; // may be negative;
9461  }
9462  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
9463  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
9464  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
9465  //next station stop
9466  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
9467  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
9468  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
9469  //first find departure time from the next stop
9470  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
9471  {
9472  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
9473  {
9476  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9477  }
9478  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
9479  {
9481  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
9482  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
9483  {
9484  // must be a departure
9485  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
9486  }
9487  }
9488  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
9489  {
9490  MinsEarly = 0;
9491  }
9492  if(MinsEarly < 0)
9493  {
9494  MinsEarly = 0;
9495  }
9496  }
9497  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
9498  {
9499  if(CurrentStopTime > 0)
9500  {
9501  TotalStopTime = CurrentStopTime + LaterStopTime;
9502  }
9503  // stopped at loc, will depart on time
9504  else
9505  {
9506  TotalStopTime = LaterStopTime - MinsDelayed;
9507  }
9508  // not stopped, will depart on time at first later stop so add the delay
9509  }
9510  else if((MinsEarly > 0) && !Stopped()) //running early
9511  {
9512  TotalStopTime = LaterStopTime + MinsEarly;
9513  }
9514  else // on time or running late
9515  {
9516  if(LaterStopTime == 0)
9517  {
9518  TotalStopTime = CurrentStopTime;
9519  }
9520  // no later stops, if stopped now will depart as soon as possible,
9521  // if not stopped no stop times to add
9522  else
9523  {
9524  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
9525  }
9526  }
9527  if(AvTrackSpeed < 30)
9528  {
9529  AvTrackSpeed = 30;
9530  }
9531  int Speed = AvTrackSpeed;
9532  if(AvTrackSpeed > int(MaxRunningSpeed))
9533  {
9534  Speed = int(MaxRunningSpeed);
9535  }
9536  if(TrainMode == Signaller)
9537  {
9538  Speed = SignallerMaxSpeed;
9539  TotalStopTime = 0;
9540  }
9541  if(DistanceToRedSignalSet)
9542  {
9543  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9544  // accel & decel taken into account in
9545  // CalcDistanceToRedSignalandStopTime
9546  // 3.6 converts Km/h to m/s & 60 converts seconds to minutes
9547  TimeToExit = -1;
9548  Utilities->CallLogPop(2079);
9549  return(TimeToAct);
9550  }
9551  else //DistanceToExitSet must be true
9552  {
9553  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
9554  // accel & decel taken into account in
9555  // CalcDistanceToRedSignalandStopTime
9556  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
9557  Utilities->CallLogPop(2370);
9558  return(-1); //no red signal so no time to act
9559  }
9560  }
9561  else // stopped not at location
9562  {
9564  {
9565  TimeToAct = 0.0;
9566  TimeToExit = -1;
9567  }
9568  if(StoppedWithoutPower) //added at v2.13.2 as this situation was missed & time to act was 0 [If train failed then covered above]
9569  {
9570  TimeToAct = -1;
9571  TimeToExit = -1;
9572  }
9573  // but if stopped at a signal & autosigs route after it then ok, provided signal not failed
9574  if(StoppedAtSignal)
9575  {
9576  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
9577  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
9578  bool NextElementFailed = Track->TrackElementAt(1548, NextElement).Failed; //added at v2.13.2
9579  int NextExitPos;
9580  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
9581  {
9582  if((NextEntryPos == 0) || (NextEntryPos == 2))
9583  // leading entry point
9584  {
9585  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
9586  {
9587  NextExitPos = 1;
9588  }
9589  else
9590  {
9591  NextExitPos = 3;
9592  }
9593  }
9594  else
9595  {
9596  NextExitPos = 0; // trailing entry point
9597  }
9598  }
9599  else
9600  {
9601  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
9602  }
9603  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
9604  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
9605  int RouteNumber; // holder for referenced value, not used
9606  if((AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !NextElementFailed)
9607  { //NextElementFailed added at v2.13.2
9608  TimeToAct = -1;
9609  TimeToExit = -1;
9610  }
9611  }
9612  Utilities->CallLogPop(2074);
9613  return(TimeToAct);
9614  }
9615 }
9616 
9617 // ---------------------------------------------------------------------------
9618 
9620 {
9621  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
9622  if(LeadElement > -1)
9623  {
9625  {
9626  Utilities->CallLogPop(2148);
9627  return(true);
9628  }
9629  }
9630  if(MidElement > -1)
9631  {
9633  {
9634  Utilities->CallLogPop(2149);
9635  return(true);
9636  }
9637  }
9638  if(LagElement > -1)
9639  {
9641  {
9642  Utilities->CallLogPop(2150);
9643  return(true);
9644  }
9645  }
9646  Utilities->CallLogPop(2151);
9647  return(false);
9648 }
9649 
9650 // ---------------------------------------------------------------------------
9651 // TTrainController
9652 // ---------------------------------------------------------------------------
9653 
9655 {
9656  OnTimeArrivals = 0;
9657  LateArrivals = 0;
9658  EarlyArrivals = 0;
9659  OnTimePasses = 0;
9660  LatePasses = 0;
9661  EarlyPasses = 0;
9662  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
9663  LateExits = 0;
9664  EarlyExits = 0;
9665  OnTimeDeps = 0;
9666  LateDeps = 0;
9667  MissedStops = 0;
9668  OtherMissedEvents = 0;
9669  UnexpectedExits = 0;
9670  NumFailures = 0;
9671  IncorrectExits = 0;
9672  SPADEvents = 0;
9673  SPADRisks = 0;
9674  CrashedTrains = 0;
9675  Derailments = 0;
9676  TotArrDepPass = 0;
9677  TotLateArrMins = 0;
9678  TotEarlyArrMins = 0;
9679  TotLatePassMins = 0;
9680  TotEarlyPassMins = 0;
9681  TotLateExitMins = 0; //added at v2.9.1
9682  TotEarlyExitMins = 0; //added at v2.9.1
9683  TotLateDepMins = 0;
9684  ExcessLCDownMins = 0;
9685  SkippedTTEvents = 0; //added at v2.11.0
9686  TTClockTime = 0; // added for v0.6
9688  // added at v1.3.0 to ensure false at start
9689  OpTimeToActUpdateCounter = 0; // new v2.2.0
9690  OpActionPanelVisible = false; // new v2.2.0
9691  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
9692  SSHigh = false;
9693  MRSHigh = false;
9694  MRSLow = false;
9695  MassHigh = false;
9696  BFHigh = false;
9697  BFLow = false;
9698  PwrHigh = false;
9699  SigSHigh = false;
9700  SigSLow = false;
9701  randomize();
9702  // to seed rand() & random() with a random number (see UpdateTrain)
9703 }
9704 
9705 // ---------------------------------------------------------------------------
9706 
9708 {
9709  for(unsigned int x = 0; x < TrainVector.size(); x++)
9710  {
9711  TrainVectorAt(32, x).DeleteTrain(4);
9712  }
9713  TrainVector.clear();
9714 }
9715 
9716 // ---------------------------------------------------------------------------
9717 
9718 void TTrainController::LogEvent(AnsiString Str)
9719 {
9720  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
9721 
9722  // restrict to last 1000 entries
9723  Utilities->EventLog.push_back(FullStr);
9724  if(Utilities->EventLog.size() > 1000)
9725  {
9726  Utilities->EventLog.pop_front();
9727  }
9728 }
9729 
9730 // ---------------------------------------------------------------------------
9731 
9733 {
9734  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
9735  bool ClockState = Utilities->Clock2Stopped;
9736  Utilities->Clock2Stopped = true;
9737  // new section dealing with Snt & Snt-sh additions
9738  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
9739  // clock tick after stops flashing
9741  {
9742  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9743  {
9744  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
9745  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
9746  TActionEventType EventType = NoEvent;
9747  if(AVEntry0.Command == "Snt")
9748  {
9749  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9750  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9751  int IncrementalMinutes = 0;
9752  int IncrementalDigits = 0;
9753  if(AVEntryLast.FormatType == Repeat)
9754  {
9755  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9756  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9757  }
9758  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9759  {
9760  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
9761  }
9762  // see above note
9763 
9764  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9765  {
9766  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
9767  if(TTOD.RunningEntry != NotStarted)
9768  {
9769  continue;
9770  }
9771 
9772 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
9773 //if so and no arrival signalled yet bypass the timetabled arrival
9774 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
9775 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
9776 
9777  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
9778  {
9779  break; // all the rest will also be greater
9780  }
9781  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
9782  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9783  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
9784  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
9785  {
9786  TTOD.TrainID = TrainVector.back().TrainID;
9787  TTOD.RunningEntry = Running;
9788  TrainVector.back().Description = TDEntry.FixedDescription; //added at v2.16.1
9789  }
9790  else if(EventType == FailTrainEntry)
9791  {
9792  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9793  }
9794  }
9795  }
9796  if(AVEntry0.Command == "Snt-sh")
9797  // just start this once, shuttle repeats take care of restarts
9798  {
9799  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9800  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9801  int IncrementalMinutes = 0;
9802  int IncrementalDigits = 0;
9803  if(AVEntryLast.FormatType == Repeat)
9804  {
9805  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9806  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9807  }
9808  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9809  {
9810  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
9811  }
9812  // see above note
9813  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
9814  if(TTOD.RunningEntry == NotStarted)
9815  {
9816  if(AVEntry0.EventTime <= TTClockTime)
9817  {
9818  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9819  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
9820  TDEntry.SignallerSpeed, false, EventType))
9821  // false for SignallerControl
9822  {
9823  TTOD.TrainID = TrainVector.back().TrainID;
9824  TTOD.RunningEntry = Running;
9825  TrainVector.back().Description = TDEntry.FixedDescription; //added at v2.16.1
9826  }
9827  else if(EventType == FailTrainEntry)
9828  {
9829  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9830  }
9831  }
9832  }
9833  }
9834  }
9835  }
9836 
9837  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
9838  // iteration, next cycle will catch up with any other pending updates
9839  if(!TrainVector.empty())
9840  {
9841  TrainAdded = false;
9842 
9843 //elapsed time investigations
9844 
9845 //elapsed time segment
9846 //double Start, End;
9847 //AnsiString ElapsedTimeReport = "";
9848 //end elasped time segment
9849  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
9850 //elapsed time segment
9851 //PerfLogForm->PerformanceLog(-1, "\n Train vector size: " + AnsiString(TrainVector.size()) + '\n');
9852 //PerfLogForm->PerformanceLog(-1, "Start time list");
9853 //end elapsed time segment
9854  for(unsigned int x = 0; x < TrainVector.size(); x++)
9855  {
9856 //elapsed time segment
9857 //Start = double(GetTime()) * 86400; //secs
9858 //end elapsed time segment
9859  TrainVectorAt(33, x).UpdateTrain(0);
9860 //elapsed time segment
9861 //End = double(GetTime()) * 86400;
9862 //ElapsedTimeReport = TrainVectorAt(-1, x).TrainDataEntryPtr->ServiceReference + AnsiString(" ") + AnsiString(int((End - Start) * 1000)); //msecs
9863 //PerfLogForm->PerformanceLog(-1, ElapsedTimeReport);
9864 //end elapsed time segment
9865 
9866 //end elapsed time investigations
9867 
9868  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
9869  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
9870  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
9871  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
9872  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
9873  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
9874  so it doesn't plot trains with TrainGone set, but left this is as does no harm
9875 
9876  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
9877  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
9878  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
9879 
9880  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
9881  */
9882  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
9883  {
9884  break; //only one exited train will be dealt with at a time (see below) so no point looking further
9885  }
9886  }
9887  // set warning flags (ManualLCDownAttentionWarning dealt with in InterfaceUnit)
9888  CrashWarning = false;
9889  DerailWarning = false;
9890  SPADWarning = false;
9891  CallOnWarning = false;
9892  SignalStopWarning = false;
9893  BufferAttentionWarning = false;
9894  TrainFailedWarning = false;
9895  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
9896  {
9897  TTrain &Train = TrainVectorAt(34, x);
9898  if(Train.Crashed)
9899  // can't use background colours for crashed & derailed because same colour
9900  {
9901  CrashWarning = true;
9902  }
9903  else if(Train.Derailed)
9904  // can't use background colours for crashed & derailed because same colour
9905  {
9906  DerailWarning = true;
9907  }
9908  else if(Train.BackgroundColour == clSPADBackground)
9909  // use colour as that changes as soon as passes signal
9910  {
9911  SPADWarning = true;
9912  }
9913  else if(Train.BackgroundColour == clTrainFailedBackground)
9914  {
9915  TrainFailedWarning = true;
9916  }
9917  else if(Train.BackgroundColour == clCallOnBackground)
9918  // use colour as also stopped at signal
9919  {
9920  CallOnWarning = true;
9921  }
9922  else if(Train.BackgroundColour == clSignalStopBackground)
9923  // use colour to distinguish from call-on
9924  {
9925  SignalStopWarning = true;
9926  }
9927  else if(Train.BackgroundColour == clBufferAttentionNeeded)
9928  // use colour to distinguish from ordinary buffer stop
9929  {
9930  BufferAttentionWarning = true;
9931  }
9932  if(Train.HasTrainGone())
9933  {
9934  AnsiString Loc = "";
9935  bool ElementFound = false;
9936  TTrackElement TE;
9937  if(Train.LagElement > -1)
9938  {
9939  TE = Track->TrackElementAt(531, Train.LagElement);
9940  ElementFound = true;
9941  }
9942  else if(Train.MidElement > -1)
9943  {
9944  TE = Track->TrackElementAt(779, Train.MidElement);
9945  ElementFound = true;
9946  }
9947  else if(Train.LeadElement > -1)
9948  {
9949  TE = Track->TrackElementAt(780, Train.LeadElement);
9950  ElementFound = true;
9951  }
9952  if(ElementFound)
9953  {
9954  if(TE.ActiveTrackElementName != "")
9955  {
9956  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9957  }
9958  else
9959  {
9960  Loc = "track element " + TE.ElementID;
9961  }
9962  }
9963  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
9964  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
9965  // need above first because may also have ActionVectorEntryPtr == "Fer"
9966  {
9967  Train.UnplotTrain(9);
9968  // added at v1.3.0 to reset signals after train removed from an autosigsroute
9970  {
9973  }
9974  // end of addition
9975  AllRoutes->RebuildRailwayFlag = true;
9976  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
9977  // correctly after a crash
9978  }
9979  else if(AVEntryPtr->Command == "Fer")
9980  {
9981  bool CorrectExit = false;
9982  if(!AVEntryPtr->ExitList.empty())
9983  {
9984  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
9985  {
9986  if(*ELIT == Train.LagElement)
9987  {
9988  CorrectExit = true;
9989  }
9990  }
9991  }
9992  if(CorrectExit)
9993  {
9994  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, "", AVEntryPtr->EventTime, AVEntryPtr->Warning);
9995  }
9996  else
9997  {
9998  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
9999  }
10000  }
10001  else
10002  {
10003  if(!AVEntryPtr->SignallerControl)
10004  {
10005  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
10006  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
10007  // -2 is marker for send messages for all remaining actions except Fer if present
10008  }
10009  else
10010  {
10011  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, "", TDateTime(0), false); // false for Warning
10012  }
10013  }
10014  Utilities->CumulativeDelayedRandMinsAllTrains += Train.CumulativeDelayedRandMinsOneTrain; //added at v2.13.0 for random delays
10015  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
10016  Train.DeleteTrain(1);
10017  TrainVector.erase(TrainVector.begin() + x);
10018  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
10019  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
10020  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
10021  }
10022  }
10023  }
10024  else
10025  {
10026  // reset all flags in case last train removed with flag set
10027  CrashWarning = false;
10028  DerailWarning = false;
10029  SPADWarning = false;
10030  CallOnWarning = false;
10031  SignalStopWarning = false;
10032  BufferAttentionWarning = false;
10033  TrainFailedWarning = false;
10034  }
10035  // update OpTimeToActMultimap
10037  {
10039  // clears entries then adds values for running trains then for continuation entries
10041  //added for multiplayer for running trains only
10042  }
10043  Utilities->Clock2Stopped = ClockState;
10044  Utilities->CallLogPop(723);
10045 }
10046 
10047 // ---------------------------------------------------------------------------
10049 {
10050  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
10051  if(!TrainVector.empty())
10052  {
10053  for(int x = TrainVector.size() - 1; x >= 0; x--)
10054  {
10055  TrainVectorAt(50, x).DeleteTrain(2);
10056  }
10057  TrainVector.clear();
10058  }
10059  if(!TrainDataVector.empty())
10060  {
10061  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
10062  {
10063  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
10064  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
10065  {
10066  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
10067  TOD.RunningEntry = NotStarted;
10068  TOD.TrainID = -1;
10069  TOD.EventReported = NoEvent;
10070  }
10071  }
10072  }
10073  Display->GetOutputLog1()->Caption = "";
10074  Display->GetOutputLog2()->Caption = "";
10075  Display->GetOutputLog3()->Caption = "";
10076  Display->GetOutputLog4()->Caption = "";
10077  Display->GetOutputLog5()->Caption = "";
10078  Display->GetOutputLog6()->Caption = "";
10079  Display->GetOutputLog7()->Caption = "";
10080  Display->GetOutputLog8()->Caption = "";
10081  Display->GetOutputLog9()->Caption = "";
10082  Display->GetOutputLog10()->Caption = "";
10083  Utilities->CallLogPop(1352);
10084 }
10085 
10086 // ---------------------------------------------------------------------------
10087 
10089 {
10090  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
10091  if(!TrainVector.empty())
10092  {
10093  for(unsigned int x = 0; x < TrainVector.size(); x++)
10094  {
10095  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
10096  { //see Kevin Smith error information for details
10097  TrainVectorAt(51, x).PlotTrain(4, Disp);
10098  }
10099  }
10100  }
10101  Utilities->CallLogPop(724);
10102 }
10103 
10104 // ---------------------------------------------------------------------------
10105 
10106 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
10107 {
10108  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
10109  if(!TrainVector.empty())
10110  {
10111  for(unsigned int x = 0; x < TrainVector.size(); x++)
10112  {
10113  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
10114  }
10115  }
10116  Utilities->CallLogPop(1707);
10117 }
10118 
10119 // ---------------------------------------------------------------------------
10120 
10122 {
10123  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
10124  if(!TrainVector.empty())
10125  {
10126  for(unsigned int x = 0; x < TrainVector.size(); x++)
10127  {
10128  TrainVectorAt(52, x).UnplotTrain(10);
10129  }
10130  }
10132  Utilities->CallLogPop(725);
10133 }
10134 
10135 // ---------------------------------------------------------------------------
10136 
10137 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
10138  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
10139  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
10140 {
10141  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
10142  "," + AnsiString(Mass) + "," + ModeStr);
10143  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
10144  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
10145 
10146  int RearExitPos = -1;
10147 
10148  for(int x = 0; x < 4; x++)
10149  {
10150  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
10151  {
10152  RearExitPos = x;
10153  }
10154  }
10155  if(RearExitPos == -1)
10156  {
10157  throw Exception("Error, RearExit == -1 in AddTrain");
10158  }
10159  bool ReportFlag = true;
10160 
10161  // used to stop repeated messages from CheckStartAllowable when split failed
10162  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
10163  {
10164  ReportFlag = false;
10165  }
10166  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
10167  {
10168  // messages sent to performance log in CheckStartAllowable if ReportFlag true
10169  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
10170  Utilities->CallLogPop(938);
10171  return(false);
10172  }
10173  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
10174  TTrainMode TrainMode = NoMode;
10175 
10176  if(ModeStr == "Timetable")
10177  {
10178  TrainMode = Timetable;
10179  }
10180  // all else gives 'None', 'Signaller' set within program
10181 
10182  if(MaxRunningSpeed < 10)
10183  {
10184  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
10185  }
10186  if(SignallerSpeed < 10)
10187  {
10188  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
10189  }
10190  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
10191  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
10192 
10193  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
10194 
10195  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
10196  // initialise here rather than in TTrain constructor as create trains
10197  // with Null TrainDataEntryPtr when loading session trains
10198  if(SignallerControl)
10199  {
10200  NewTrain->TimetableFinished = true;
10201  NewTrain->SignallerStoppingFlag = false;
10202  NewTrain->TrainMode = Signaller;
10203  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
10204  {
10205  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
10206  }
10208  }
10209  // deal with starting conditions:-
10210  // unlocated Snt: just report entry & advance pointer
10211  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
10212  // Sns doesn't need a new train
10213  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
10214  // covers all above located starts
10215  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
10216  // wouldn't have accepted the timetable
10217  {
10218  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
10219  // StoppedAtBuffers is set in UpdateTrain()
10220  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
10221  // buffer end must be ahead of train or would have failed start position check
10222  {
10223  NewTrain->StoppedAtLocation = true;
10224  NewTrain->PlotStartPosition(0);
10226  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, "", NewTrain->ActionVectorEntryPtr->EventTime,
10227  NewTrain->ActionVectorEntryPtr->Warning);
10228  if(!SignallerControl) // don't advance if SignalControlEntry
10229  {
10230  NewTrain->ActionVectorEntryPtr++;
10231  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
10232  }
10233  NewTrain->LastActionTime = TTClockTime;
10234  }
10235  // else a through station stop
10236  else
10237  {
10238  NewTrain->StoppedAtLocation = true;
10239  NewTrain->PlotStartPosition(10);
10241  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, "", NewTrain->ActionVectorEntryPtr->EventTime,
10242  NewTrain->ActionVectorEntryPtr->Warning);
10243  if(!SignallerControl) // don't advance if SignalControlEntry
10244  {
10245  NewTrain->ActionVectorEntryPtr++;
10246  }
10247  NewTrain->LastActionTime = TTClockTime;
10248  }
10249  }
10250  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
10251  {
10252  NewTrain->PlotStartPosition(11);
10253  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
10254  AnsiString Loc = "";
10255  if(TE.ActiveTrackElementName != "")
10256  {
10257  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
10258  }
10259  else
10260  {
10261  Loc = "track element " + TE.ElementID;
10262  }
10263  if(TE.TrackType == Continuation)
10264  {
10265  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, "", NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10266  }
10267  else
10268  {
10269  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, "", NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
10270  }
10271  if(!SignallerControl) // don't advance if SignalControlEntry
10272  {
10273  NewTrain->ActionVectorEntryPtr++;
10274  }
10275  NewTrain->LastActionTime = TTClockTime;
10276  // no need to set LastActionTime for an unlocated entry
10277  }
10278  // cancel a wrong-direction route if either element of train starts on one
10279  if(NewTrain->LeadElement > -1)
10280  {
10281  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
10282  }
10283  if(NewTrain->MidElement > -1)
10284  {
10285  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
10286  }
10287  // set signals for a right-direction autosigs route for either element of train on one
10288  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
10289  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
10290  int RouteNumber = -1;
10291  bool SignalsSet = false;
10292 
10293  if(NewTrain->LeadElement > -1)
10294  {
10295  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10296  {
10297  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10298  int RouteStartPosition;
10299  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10300  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
10301  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
10302  if(FirstPair.first == RouteNumber)
10303  {
10304  RouteStartPosition = FirstPair.second;
10305  }
10306  else if(SecondPair.first == RouteNumber)
10307  {
10308  RouteStartPosition = SecondPair.second;
10309  }
10310  else
10311  {
10312  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
10313  }
10314  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
10315  SignalsSet = true;
10316  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10317  }
10318  else if(RouteNumber > -1) // non-autosigsroute
10319  {
10320  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
10321  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10322  int FirstELinkPos = TempPDE.GetELinkPos();
10323  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
10324  {
10325  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10326  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
10327  }
10328  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
10329  {
10330  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10331  // remove the last element under LeadElement
10332  }
10333  AllRoutes->RebuildRailwayFlag = true;
10334  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10335  // now deal with a rear linked autosigs route
10336  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
10337  {
10338  int LinkedRouteNumber = -1;
10339  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
10340  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10341  {
10342  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
10343  // this is ok as here we are setting signals from the start of the route
10344  }
10345  }
10346  SignalsSet = true;
10347  }
10348  }
10349  if(NewTrain->MidElement > -1)
10350  // if entering at a continuation MidElement == -1
10351  {
10352  // this is included in case a train starts with LeadElement on no route and MidElement on a route
10353  if(!SignalsSet)
10354  {
10355  RouteNumber = -1;
10356  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
10357  {
10358  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
10359  int RouteStartPosition;
10360  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
10361  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
10362  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
10363  if(FirstPair.first == RouteNumber)
10364  {
10365  RouteStartPosition = FirstPair.second;
10366  }
10367  else if(SecondPair.first == RouteNumber)
10368  {
10369  RouteStartPosition = SecondPair.second;
10370  }
10371  else
10372  {
10373  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
10374  }
10375  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
10376  SignalsSet = true;
10377  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
10378  }
10379  else if(RouteNumber > -1) // non-autosigsroute
10380  {
10381  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
10382  int FirstTVPos = TempPDE.GetTrackVectorPosition();
10383  int FirstELinkPos = TempPDE.GetELinkPos();
10384  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
10385  {
10386  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10387  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
10388  }
10389  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
10390  {
10391  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
10392  // remove the last element under LeadElement
10393  }
10394  AllRoutes->RebuildRailwayFlag = true;
10395  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
10396  // now deal with a rear linked autosigs route
10397  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
10398  {
10399  int LinkedRouteNumber = -1;
10400  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
10401  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
10402  {
10403  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
10404  // this is ok as now we are setting signals from the start of the route
10405  }
10406  }
10407  }
10408  }
10409  }
10410  TrainVector.push_back(*NewTrain);
10411  Utilities->CallLogPop(731);
10412  return(true);
10413 }
10414 
10415 // ---------------------------------------------------------------------------
10416 
10417 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
10418 {
10419  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
10420  AnsiString(TrackVectorNumber));
10421  int VecPos = -1;
10422 
10423  for(unsigned int x = 0; x < TrainVector.size(); x++)
10424  {
10425  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
10426  {
10427  VecPos = x;
10428  }
10429  }
10430  if(VecPos == -1)
10431  {
10432  throw Exception("Error, VecPos not set in EntryPos");
10433  }
10434  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
10435  {
10436  Utilities->CallLogPop(734);
10437  return(TrainVectorAt(3, VecPos).LeadEntryPos);
10438  }
10439  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
10440  {
10441  Utilities->CallLogPop(735);
10442  return(TrainVectorAt(5, VecPos).MidEntryPos);
10443  }
10444  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
10445  {
10446  Utilities->CallLogPop(736);
10447  return(TrainVectorAt(7, VecPos).LagEntryPos);
10448  }
10449  Utilities->CallLogPop(737);
10450  return(-1);
10451 }
10452 
10453 // ---------------------------------------------------------------------------
10454 
10456 {
10457  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
10458  for(unsigned int x = 0; x < TrainVector.size(); x++)
10459  {
10460  if(TrainVectorAt(53, x).TrainID == TrainID)
10461  {
10462  Utilities->CallLogPop(738);
10463  return(TrainVectorAt(54, x));
10464  }
10465  }
10466  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
10467 }
10468 
10469 // ---------------------------------------------------------------------------
10470 
10471 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
10472 // return true if find the train (added at v2.4.0 as can select a removed train in
10473 // ActionsDueListBox before it updates - reported by LiWinDom in error report 23/04/20)
10474 {
10475  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
10476  for(unsigned int x = 0; x < TrainVector.size(); x++)
10477  {
10478  if(TrainVectorAt(69, x).TrainID == TrainID)
10479  {
10480  Utilities->CallLogPop(2152);
10481  return(true);
10482  }
10483  }
10484  Utilities->CallLogPop(2153);
10485  return(false);
10486 }
10487 
10488 // ---------------------------------------------------------------------------
10489 
10490 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
10491 {
10492  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
10493  Utilities->Format96HHMMSS(Time));
10494  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
10495 
10496  Utilities->CallLogPop(2061);
10497  return(RepeatTime);
10498 }
10499 
10500 // ---------------------------------------------------------------------------
10501 
10502 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
10503 // Enter with Ptr pointing to first action to be listed (i.e. next action)
10504 {
10505  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
10506  AnsiString RetStr = "", PartStr = "";
10507  int Count = 0;
10508  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
10509 
10510  Ptr--; // because incremented at start of loop
10511  do
10512  {
10513  Ptr++;
10514  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
10515  {
10516  continue; // move past the starting entry
10517  }
10518  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
10519  {
10520  break;
10521  }
10522  if(Ptr->SignallerControl)
10523  {
10524  RetStr = "Train under signaller control";
10525  break;
10526  }
10527  if(Ptr->FormatType == TimeTimeLoc)
10528  {
10529  if(Ptr->ArrivalTime == Ptr->DepartureTime)
10530  {
10531  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
10532  }
10533  else
10534  {
10535  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
10536  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10537  Count++; // because there are 2 entries
10538  }
10539  }
10540  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
10541  {
10542  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
10543  }
10544  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
10545  {
10546  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
10547  }
10548  else if(Ptr->FormatType == PassTime) // new
10549  {
10550  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
10551  }
10552  else if(Ptr->Command == "Fns")
10553  {
10554  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10555  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10556  PartStr = ControllerGetNewServiceDepartureInfo(11, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, IncDig, PartStr); //if there is a next service this adds the new service departure time to PartStr
10557  }
10558  else if(Ptr->Command == "F-nshs")
10559  {
10560  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10561  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
10562  PartStr = ControllerGetNewServiceDepartureInfo(13, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, IncDig, PartStr); //if there is a next service this adds the new service departure time to RetStr
10563  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
10564  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
10565  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
10566  }
10567 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
10568  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10569  {
10570  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10571  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10572  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10573  PartStr = ControllerGetNewServiceDepartureInfo(15, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, IncDig, PartStr); //if there is a next service this adds the new service departure time to RetStr
10574  }
10575  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10576  {
10577  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10578  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
10579  PartStr = ControllerGetNewServiceDepartureInfo(17, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, IncDig, PartStr); //if there is a next service this adds the new service departure time to RetStr
10580  }
10581  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
10582  {
10583  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
10584  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
10585  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
10586  PartStr = ControllerGetNewServiceDepartureInfo(19, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, IncDig, PartStr); //if there is a next service this adds the new service departure time to RetStr
10587  }
10588  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
10589  {
10590  PartStr = "Terminate at " + Ptr->LocationName;
10591  }
10592  else if(Ptr->Command == "Frh")
10593  {
10594  PartStr = "Terminate at " + Ptr->LocationName;
10595  }
10596  else if(Ptr->Command == "Fer")
10597  {
10598  AnsiString AllowedExits;
10599  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
10600  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
10601  }
10602  else if(Ptr->Command == "Fjo")
10603  {
10604  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
10605  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10606  }
10607  else if(Ptr->Command == "jbo")
10608  {
10609  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
10610  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10611  }
10612  else if(Ptr->Command == "fsp")
10613  {
10614  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
10615  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10616  }
10617  else if(Ptr->Command == "rsp")
10618  {
10619  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
10620  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
10621  }
10622  else if(Ptr->Command == "cdt")
10623  {
10624  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
10625  }
10626  else if(Ptr->Command == "dsc")
10627  {
10628  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(27, Ptr->EventTime, RepNum, IncMins)) + ": Change description at " + Ptr->LocationName;
10629  }
10630  if(RetStr != "")
10631  {
10632  RetStr = RetStr + '\n' + PartStr;
10633  }
10634  else
10635  {
10636  RetStr = PartStr;
10637  }
10638  Count++;
10639  }
10640  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
10641  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
10642  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
10643  // forward as anyone should wish to see without looking at the full timetable
10644  Utilities->CallLogPop(2072);
10645  return(RetStr);
10646 }
10647 
10648 // ---------------------------------------------------------------------------
10649 
10651  TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, int IncrementalDigits, AnsiString RetStr)
10652 { //no delays as train not entered yet //above added IncrementalDigits at v2.18.0 for GetRepeatHeadCode
10653  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - TDEPtr->ActionVector.begin()) + ","
10654  + AnsiString(RptNum) + ",ControllerGetNewServiceDepartureInfo," + TDEPtr->HeadCode);
10655  AnsiString DepTime = "", EventTime = "";
10656  bool CDTFlag = false; //reports if train changes direction before departs
10657  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
10658  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
10659  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
10660  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
10661  {
10662  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
10663  {
10664  TowardsLocation = AVI->LocationName;
10665  }
10666  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
10667  {
10668  TTrackElement TE = Track->TrackElementAt(1453, (AVI->ExitList.front()));
10669  if(TE.ActiveTrackElementName != "")
10670  {
10671  TowardsLocation = TE.ActiveTrackElementName;
10672  }
10673  else
10674  {
10675  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
10676  }
10677  }
10678  }
10679  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
10680  {
10681  if(AVI->Command == "cdt")
10682  {
10683  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
10684  continue;
10685  }
10686  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
10687  {
10688  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
10689  RetStr += "\nNew service splits at " + EventTime;
10690  Utilities->CallLogPop(2237);
10691  return(RetStr);
10692  }
10693  if(AVI->Command == "jbo")
10694  {
10695  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(30, AVI->EventTime, RptNum, IncrementalMinutes));
10696  RetStr += "\nNew service joined by " + GetRepeatHeadCode(70, AVI->OtherHeadCode, RptNum, IncrementalDigits) + " at " + EventTime;
10697  Utilities->CallLogPop(2238); //above added GetRepeatHeadCode at v2.18.0 (was just AVI->OtherHeadCode before)
10698  return(RetStr);
10699  }
10700  if((AVI->Command == "Fns") || (AVI->Command == "F-nshs") || (AVI->Command == "Fns-sh"))
10701  {
10702  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(31, AVI->EventTime, RptNum, IncrementalMinutes));
10703  RetStr += "\nNew service finishes and forms another new service at " + EventTime;
10704  Utilities->CallLogPop(2607);
10705  return(RetStr);
10706  }
10707  if(AVI->Command == "Fjo")
10708  {
10709  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
10710  RetStr += "\nNew service finishes and joins " + TrainController->GetRepeatHeadCode(71, AVI->OtherHeadCode, RptNum, IncrementalDigits) + " at " + EventTime;
10711  Utilities->CallLogPop(2608); //above added GetRepeatHeadCode at v2.18.0 (was just AVI->OtherHeadCode before)
10712  return(RetStr);
10713  }
10714  if(AVI->Command == "Frh")
10715  {
10716  RetStr += "\nNew service finishes and remains at location.";
10717  Utilities->CallLogPop(2609);
10718  return(RetStr);
10719  }
10720  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
10721  {
10722  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
10723  if(CDTFlag)
10724  {
10725  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10726  {
10727  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
10728  }
10729  else
10730  {
10731  RetStr += "\nNew service changes direction then departs at " + DepTime;
10732  }
10733  }
10734  else
10735  {
10736  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10737  {
10738  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
10739  }
10740  else
10741  {
10742  RetStr += "\nNew service departs at " + DepTime;
10743  }
10744  }
10745  Utilities->CallLogPop(2239);
10746  return(RetStr);
10747  }
10748  }
10749  Utilities->CallLogPop(2223);
10750  return(RetStr);
10751 }
10752 
10753 // ---------------------------------------------------------------------------
10754 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
10755 /*
10756  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
10757  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
10758  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
10759 
10760  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
10761  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
10762  user wishes
10763 
10764  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10765  descriptive text or anything user wishes
10766  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10767  be ignored) is taken as the timetable start time.
10768  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10769  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10770  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10771  within the timetable if required.
10772  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10773  services)
10774  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10775  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10776 
10777  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10778  text timetable file easier
10779 
10780  form:-
10781  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10782  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10783  then multiple entries, separated by commas, of the form:-
10784 
10785  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10786  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
10787  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
10788 
10789  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
10790  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
10791  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
10792 
10793  HH:MM;Command (cdt) }TimeCmd }
10794  HH:MM;Command;Description (dsc) }TimeCmdDescription }
10795  HH:MM;Location (arr & dep) }TimeLoc }
10796  HH:MM;HH:MM;Location }TimeTimeLoc }
10797  HH:MM;pas;Location }PassTime }
10798  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
10799  HH:MM;Fer;set of allowable IDs }ExitRailway }
10800  Command (Frh only) }FinRemHere }
10801 
10802  R;mm;dd;nn. Repeat Repeat entry
10803 
10804  Formats:
10805 
10806  Command only: Frh
10807  Time;Command: cdt
10808  Time;Command;Description dsc
10809  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
10810  Time;Command;2 Element IDs: Snt
10811  Time;Comand;n Element IDs: Fer
10812  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
10813  Time;Command;2 Element IDs;Headcode Snt-sh
10814  Time;Command;Location pas
10815  Time;Location Arr Dep
10816  Time;Time;Location Arr & dep together
10817 
10818  10 Non-linked entries: Snt (located or unlocated); pas; cdt; dsc; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
10819 
10820  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
10821  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
10822  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
10823 
10824  4 2x Linked entries, all shuttles:
10825 
10826  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
10827  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
10828  -> Remain Here (at finish location after all repeats)
10829  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
10830  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
10831 
10832  Moving/AtLoc states:-
10833 
10834  Successor state Type
10835 
10836  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/dsc/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
10837  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
10838  Sfs AtLoc )
10839  Sns AtLoc ) Start
10840  Sns-fsh AtLoc )
10841  Snt-sh AtLoc )
10842  Sns-sh AtLoc )
10843 
10844  pas Moving )
10845  jbo AtLoc )
10846  fsp AtLoc )
10847  rsp AtLoc ) Intermediate
10848  cdt AtLoc )
10849  dsc AtLoc )
10850  TimeLoc arr Moving (bef), AtLoc (aft) )
10851  TimeLoc dep AtLoc (bef), Moving (aft) )
10852  TimeTimeLoc Moving )
10853 
10854  Fns Repeat/Nothing)
10855  Fjo Repeat/Nothing)
10856  Frh Repeat/Nothing)
10857  Fer Repeat/Nothing) Finish
10858  Frh-sh Repeat )
10859  Fns-sh Repeat )
10860  F-nshs Nothing )
10861 
10862  Descriptions:
10863  Snt New train
10864  Sfs New service from split
10865  Sns New service from another service
10866  Sns-fsh New non-repeating service from a shuttle service
10867  Snt-sh New shuttle train at a timetabled stop
10868  Sns-sh New shuttle service from a feeder service
10869 
10870  pas Pass
10871  jbo Be joined by another train
10872  fsp Front split
10873  rsp Rear split
10874  cdt Change direction of train
10875  dsc Change description of train
10876  TimeLoc arr Arrival
10877  TimeLoc dep Departure
10878  TimeTimeLoc Arrival and departure
10879 
10880  Fns Finish & form a new service
10881  Fjo Finish & join another train
10882  Frh Finish & remain here
10883  Fer Finish & exit railway
10884  Frh-sh Finish & repeat shuttle, finally remain here
10885  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
10886  F-nshs Finish & form a shuttle feeder service
10887 */
10888 
10889 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
10890 {
10891  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
10892  // a line that is too long; timetable containing too few lines; and timetable failed to open.
10893  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
10894  // new for v0.2b
10895  // compile ActiveTrackElementNameMap
10896  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
10897 
10899  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
10900  {
10901  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
10903  == Track->ContinuationNameMap.end())
10904  {
10905  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
10906  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
10907  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
10908  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
10909  }
10910  }
10912  // end of new section
10913  std::ifstream TTBLFile(FileName, std::ios_base::binary);
10914 
10915  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
10916  if(TTBLFile.is_open())
10917  {
10918  char *TrainTimetableString = new char[10000];
10919  // enough for over 200 stations, should be adequate!
10920  bool EndOfFile = false;
10921  int Count = 0;
10922  // counts 'relevant' lines, i.e ignores any before the start time on its own line
10923  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10924  // delimiter is '\0' as it's an AnsiString
10925  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10926  // file empty - stores a null in 1st position if doesn't load any characters
10927  {
10928  // may still have eof even if read a line (no CRLF at end), and
10929  // if so need to process it
10930  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
10931  TTBLFile.close();
10932  delete[] TrainTimetableString;
10933  Utilities->CallLogPop(1611);
10934  return(false);
10935  }
10936  AnsiString OneLine(TrainTimetableString);
10937  bool FinalCallFalse = false;
10938  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10939  // get rid of lines before the start time
10940  {
10941  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
10942  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10943  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10944  // stores a null in 1st position if doesn't load any characters
10945  {
10946  // may still have eof even if read a line (no CRLF at end), and
10947  // if so need to process it
10948  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
10949  TTBLFile.close();
10950  delete[] TrainTimetableString;
10951  Utilities->CallLogPop(772);
10952  return(false);
10953  }
10954  OneLine = AnsiString(TrainTimetableString);
10955  }
10956  // here when have accepted the start time
10957  Count++; // increment past the start time
10958  while(!EndOfFile)
10959  {
10960  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10961  // get next line after start time
10962  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10963  // stores a null in 1st position if doesn't load any characters
10964  {
10965  // may still have eof even if read a line (no CRLF at end), and
10966  // if so need to process it
10967  EndOfFile = true;
10968  OneLine = "";
10969  }
10970  else
10971  {
10972  OneLine = AnsiString(TrainTimetableString);
10973  }
10974  if(OneLine.Length() > 9999)
10975  {
10976  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
10977  TTBLFile.close();
10978  delete[] TrainTimetableString;
10979  Utilities->CallLogPop(789);
10980  return(false);
10981  }
10982  bool FinalCallFalse = false;
10983  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10984  // false for FinalCall - just checking at this stage
10985  {
10986  TTBLFile.close();
10987  delete[] TrainTimetableString;
10988  Utilities->CallLogPop(770);
10989  return(false);
10990  }
10991  if(EndOfFile && (Count < 2))
10992  // Timetable must contain at least two relevant lines, one for start time and at least one train
10993  {
10994  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
10995  TTBLFile.close();
10996  delete[] TrainTimetableString;
10997  Utilities->CallLogPop(771);
10998  return(false);
10999  }
11000  Count++;
11001  }
11002  delete[] TrainTimetableString;
11003  TTBLFile.close();
11004  } // if(TTBLFile.is_open())
11005  else
11006  {
11007  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
11008  Utilities->CallLogPop(2154);
11009  return(false);
11010  }
11011  Utilities->CallLogPop(753);
11012  return(true);
11013 }
11014 
11015 // ---------------------------------------------------------------------------
11016 
11017 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
11018  bool CheckLocationsExistInRailway) // return true for success
11019 
11020 /* Format:
11021  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
11022  descriptive text or anything user wishes
11023  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
11024  be ignored) is taken as the timetable start time.
11025  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
11026  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
11027  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
11028  within the timetable if required.
11029  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
11030  services)
11031  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
11032  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
11033 
11034  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
11035  text timetable file easier
11036 
11037  form:-
11038  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
11039  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
11040  then multiple entries, separated by commas, of the form:-
11041 
11042  Format FormatType
11043  [W]HH:MM;Command (cdt) }TimeCmd }
11044  [W]HH:MM;dsc;new description }TimeCmdDescription }
11045  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
11046  [W]HH:MM;pas;Location }PassTime }
11047  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
11048  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
11049  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
11050  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
11051  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
11052  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
11053  [W]HH:MM;Fns-sh;Details }FSHNewService }
11054  [W]HH:MM;Location (arr & dep) }TimeLoc }
11055  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
11056  Command (Frh only) }FinRemHere }
11057 
11058  R;mm;dd;nn. Repeat Repeat entry
11059 
11060  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
11061  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
11062  at location; or (c) departure time if train already at location (including train started at location either as a new
11063  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
11064  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
11065  minutes, incremental train headcode last 2 digits, and number of repeats.
11066 
11067  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
11068  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
11069  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
11070  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
11071  (it's for a shuttle train to return to depot at end of services)
11072 
11073  Command/Location & details are as follows:-
11074 
11075  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
11076  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
11077  2E44 in its Sfs entry. All these are checked.
11078  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
11079 
11080  Start commands:-
11081  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
11082  with loc as a start entry can't have a location as details)
11083  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
11084  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
11085  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
11086  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
11087  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
11088  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
11089  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
11090  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
11091  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
11092  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
11093  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
11094 
11095  Intermediate commands:-
11096  Time - Location (TimeLoc), can be arrival or departure depending on context
11097  Time Time location (TimeTimeLoc), arrival and departure
11098  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
11099  pas (PassTime), Time;pas;Location
11100  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
11101  joining train's finish details must correspond or the file check will fail
11102  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
11103  new train - that train's starting information must correspond)
11104  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
11105  new train - that train's starting information must correspond)
11106  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
11107  dsc (TimeCmdDescription) = Change description of train = new description only, no train creation
11108 
11109  Finish commands:-
11110  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
11111  creation)
11112  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
11113  shuttle headcode (no train creation)
11114  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
11115  may have to wait for it), details = new headcode (delete train)
11116  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
11117  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
11118  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
11119  here
11120  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
11121  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
11122 
11123  Repeat:-
11124  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
11125  headcodes - it is up to user to avoid duplicates if he/she wishes to.
11126 
11127  Checks carried out with error messages in this function:-
11128  At least one comma in a service line (it's based on a .csv file)
11129  No entries following train information;
11130  At least one comma in remainder after train information (i.e at least a start and a finish entry);
11131  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
11132  First entry not a start entry;
11133  Train information incomplete before a start entry;
11134  Entry follows a finish entry but doesn't begin with 'R';
11135  SplitEntry returns false in a finish entry - message repeats the entry for information;
11136  Last action entry isn't a finish entry.
11137 
11138  Function returns false with no message if:-
11139  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
11140  time is found at all then an error message is given in the calling function);
11141  SplitTrainInfo returns false (message given in called function);
11142  SplitRepeat returns false (message given in called function).
11143 */
11144 {
11145  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
11146  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
11147  TTrainDataEntry TempTrainDataEntry;
11148 
11149  EndOfFile = false;
11150  StripSpaces(0, OneLine);
11151  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
11152  // semicolons within the line
11153  ServiceReference = "";
11154  if(OneLine != "")
11155  {
11156  if(OneLine[1] != '*')
11157  {
11158  int SCPos = OneLine.Pos(';');
11159  if(SCPos == 0)
11160  {
11161  ServiceReference = OneLine.SubString(1, 8);
11162  }
11163  else
11164  {
11165  ServiceReference = OneLine.SubString(1, (SCPos - 1));
11166  }
11167  }
11168  }
11169  bool AllCommas = true;
11170 
11171  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
11172  {
11173  if(OneLine[x] != ',')
11174  {
11175  AllCommas = false;
11176  }
11177  }
11178  if(AllCommas || (OneLine == ""))
11179  {
11180  if(Count > 0)
11181  {
11182  EndOfFile = true;
11183  // returns true for a blank line (or a line of all commas) - treated as end of file
11184  Utilities->CallLogPop(1018);
11185  return(true);
11186  }
11187  else // count == 0 so not yet found a start time, no message to be given
11188  {
11189  Utilities->CallLogPop(754);
11190  return(false);
11191  }
11192  }
11193  AnsiString First = "", Second = "", Third = "", Fourth = "";
11194  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
11195  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
11196  TDateTime StartTime(0);
11197  TNumList ExitList;
11198  bool Warning = false;
11199 
11200  if(Count == 0) // no start time found yet
11201  {
11202 /* dropped at v0.6b
11203  AnyHeadCodeValid = false;
11204  if(OneLine.SubString(6,5) == ";0000")
11205  {
11206  AnyHeadCodeValid = true;
11207  }
11208 */
11209  if(!CheckTimeValidity(0, OneLine, StartTime))
11210  {
11211  // no message is given for an invalid time as it's assumed to be an irrelevant line
11212  // if no start time is found at all then an error message is given in the calling function
11213  // AnyHeadCodeValid = false;
11214  Utilities->CallLogPop(755);
11215  return(false);
11216  }
11217  if(FinalCall) // here if start time valid
11218  {
11219  TTClockTime = StartTime;
11220  TimetableStartTime = StartTime;
11221  }
11222  }
11223  else
11224  {
11225  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
11226  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
11227  double MaxBrakeRate = 0;
11228  double PowerAtRail = 0;
11229  int SignallerSpeed = 0;
11230  if(OneLine[1] == '*')
11231  {
11232  Utilities->CallLogPop(1581);
11233  return(true);
11234  // ignore any line beginning with '*' but return true as there is no error
11235  }
11236  int Pos = OneLine.Pos(',');
11237  if(Pos == 0)
11238  {
11239  int SubStringLength = 20;
11240  if(OneLine.Length() < 20)
11241  {
11242  SubStringLength = OneLine.Length();
11243  }
11244  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
11245  Utilities->CallLogPop(766);
11246  return(false);
11247  }
11248  TrainInfoStr = OneLine.SubString(1, Pos - 1);
11249  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
11250  GiveMessages)) // error messages given in SplitTrainInfo
11251  {
11252  Utilities->CallLogPop(773);
11253  return(false);
11254  }
11255  if(FinalCall)
11256  {
11257  // store Train info - conversions done in SplitTrainInfo
11258  // only headcode mandatory for continuing services
11259  //HeadCode = ServiceReference until final section of SecondPassActions
11260  TempTrainDataEntry.HeadCode = HeadCode;
11261  TempTrainDataEntry.ServiceReference = HeadCode;
11262  TempTrainDataEntry.FixedDescription = Description; //name changed at v2.16.1
11263  TempTrainDataEntry.ExplicitDescription = false;
11264  if(Description != "")
11265  {
11266  TempTrainDataEntry.ExplicitDescription = true;
11267  }
11268  TempTrainDataEntry.StartSpeed = StartSpeed;
11269  TempTrainDataEntry.Mass = Mass;
11270  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
11271  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
11272  TempTrainDataEntry.PowerAtRail = PowerAtRail;
11273  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
11274  TTrainOperatingData TempTrainOperatingData;
11275  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
11276  }
11277  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
11278  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
11279  // so strip them off
11280  while(NewRemainder[NewRemainder.Length()] == ',')
11281  {
11282  if(NewRemainder.Length() > 1)
11283  {
11284  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
11285  }
11286  else
11287  {
11288  NewRemainder = "";
11289  break;
11290  }
11291  }
11292  // check if zero length & fail if so
11293  if(NewRemainder == "")
11294  {
11295  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
11296  Utilities->CallLogPop(769);
11297  return(false);
11298  }
11299  // now have one more entry than there are commas
11300  int CommaCount = 0;
11301  for(int x = 1; x < NewRemainder.Length() + 1; x++)
11302  {
11303  if(NewRemainder[x] == ',')
11304  {
11305  CommaCount++;
11306  }
11307  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
11308  if(CommaCount == 0)
11309  {
11310  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
11311  {
11312  int SubStringLength = 20;
11313  if(OneLine.Length() < 20)
11314  {
11315  SubStringLength = OneLine.Length();
11316  }
11317  TimetableMessage(GiveMessages,
11318  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
11319  OneLine.SubString(1, SubStringLength) + "'....");
11320  Utilities->CallLogPop(783);
11321  return(false);
11322  }
11323  }
11324  AnsiString OneEntry = "";
11325  TTimetableFormatType FormatType;
11326  TTimetableSequenceType SequenceType;
11327  TTimetableLocationType LocationType;
11328  TTimetableShuttleLinkType ShuttleLinkType;
11329  bool FinishFlag = false;
11330 // bool NewTrain = false;//added at v2.14.0 to record created trains for later zero power checks <-- not needed after zero power restriction dropped
11331  for(int x = 0; x < CommaCount + 1; x++)
11332  {
11333  if((CommaCount == 0) || (x < CommaCount))
11334  // i.e. train entered under signaller control with no repeats, or entry is not the last,
11335  // in which case there's a comma & finish element or repeat still to come this entry could
11336  // be a finish but can't be a repeat
11337  {
11338  if(CommaCount == 0)
11339  {
11340  OneEntry = NewRemainder;
11341  NewRemainder = "";
11342  }
11343  else
11344  {
11345  Pos = NewRemainder.Pos(',');
11346  OneEntry = NewRemainder.SubString(1, Pos - 1);
11347  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
11348  }
11349  First = "";
11350  Second = "";
11351  Third = "";
11352  Fourth = "";
11353  RearStartOrRepeatMins = 0;
11354  FrontStartOrRepeatDigits = 0;
11355  NumberOfRepeats = 0;
11356  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11357  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11358  {
11359  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
11360  Utilities->CallLogPop(756);
11361  return(false);
11362  }
11363 /* not needed at v2.19.0
11364  if((Second == "Snt") || (Second == "Snt-sh")) //added at v2.14.0, see above
11365  {
11366  NewTrain = true; not needed when zero power restrictions removed
11367  }
11368 */
11369  // check if warning for Frh or Fjo & reject
11370  if(Warning && (Second == "Frh"))
11371  {
11372  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
11373  Utilities->CallLogPop(1793);
11374  return(false);
11375  }
11376  if(Warning && (Second == "Fjo"))
11377  {
11378  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11379  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
11380  Utilities->CallLogPop(1794);
11381  return(false);
11382  }
11383  //below added at v2.14.0 to prevent unpowered trains attempting to be joined by (Second == jbo), split (Second -- fsp or rsp),
11384  //or change direction. Form a new service dealt with below for zero power as it's a finish event. <-- removed at v2.19.0
11385 
11386 /* restriction removed at v2.19.0
11387  if(NewTrain && (PowerAtRail < 1) && (Second == "jbo"))
11388  {
11389  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11390  "': a train created without power can't 'be joined by' another train (i.e. can't include command 'jbo'), "
11391  "use command 'Fjo' (i.e. 'join' another train) instead immediately after the line containing 'Snt', and use "
11392  "command 'jbo' for the train it is to join.");
11393  Utilities->CallLogPop(2545);
11394  return(false);
11395  }
11396  if(NewTrain && (PowerAtRail < 1) && ((Second == "fsp") || (Second == "rsp")))
11397  {
11398  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11399  "': a train created without power can't split.");
11400  Utilities->CallLogPop(2546);
11401  return(false);
11402  }
11403  if(NewTrain && (PowerAtRail < 1) && (Second == "cdt"))
11404  {
11405  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11406  "': a train created without power can't change direction under timetable control.");
11407  Utilities->CallLogPop(2547);
11408  return(false);
11409  }
11410 */
11411  //end of new additions
11412  if(x == 0) // should be start event
11413  {
11414  if(SequenceType != StartSequence)
11415  {
11416  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
11417  Utilities->CallLogPop(784);
11418  return(false);
11419  }
11420  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
11421  {
11422  if(NewRemainder[1] != 'R')
11423  {
11424  TimetableMessage(GiveMessages,
11425  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
11426  OneEntry + "'");
11427  Utilities->CallLogPop(787);
11428  return(false);
11429  }
11430  }
11431  if((Second == "Snt") || (Second == "Snt-sh"))
11432  // need full train information including non-default values for at least HeadCode, Description,
11433  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
11434  {
11435  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
11436  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
11437  {
11438  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
11439  OneEntry + "'");
11440  Utilities->CallLogPop(1783);
11441  return(false);
11442  }
11443  }
11444  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
11445  // service continuation - need at least non-default value for HeadCode
11446  {
11447  if(HeadCode == "")
11448  {
11449  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11450  OneEntry + "'");
11451  Utilities->CallLogPop(788);
11452  return(false);
11453  }
11454  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
11455  {
11456  TimetableMessage(GiveMessages,
11457  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
11458  OneEntry + "'");
11459  Utilities->CallLogPop(843);
11460  return(false);
11461  }
11462  }
11463  }
11464  if(SequenceType == FinishSequence)
11465  {
11466  FinishFlag = true;
11467  // marker for only permitted additional entry being a repeat, only needed if the
11468  // finish entry is not the last entry
11469  }
11470  if(FinalCall)
11471  {
11472  // interpret & add to ActionVector
11473  TDateTime TempTime;
11474  TActionVectorEntry ActionVectorEntry;
11475  ActionVectorEntry.FormatType = FormatType;
11476  ActionVectorEntry.LocationType = LocationType;
11477  ActionVectorEntry.SequenceType = SequenceType;
11478  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11479  ActionVectorEntry.Warning = Warning;
11480  if(FormatType == TimeLoc)
11481  {
11482  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
11483  {
11484  ;
11485  } // these will all be true as final call
11486 
11487  ActionVectorEntry.LocationName = Second;
11488  }
11489  else if(FormatType == PassTime) // new
11490  {
11491  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
11492  {
11493  ;
11494  }
11495  ActionVectorEntry.Command = Second;
11496  ActionVectorEntry.LocationName = Third;
11497  }
11498  else if(FormatType == TimeTimeLoc)
11499  {
11500  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
11501  {
11502  ;
11503  }
11504  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
11505  {
11506  ;
11507  }
11508  ActionVectorEntry.LocationName = Third;
11509  }
11510  else if(FormatType == TimeCmd)
11511  {
11512  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
11513  {
11514  ;
11515  }
11516  ActionVectorEntry.Command = Second;
11517  }
11518  else if(FormatType == ExitRailway)
11519  {
11520  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
11521  {
11522  ;
11523  }
11524  ActionVectorEntry.Command = Second;
11525  ActionVectorEntry.ExitList = ExitList;
11526  }
11527  else if(FormatType == StartNew)
11528  {
11529  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
11530  {
11531  ;
11532  }
11533  ActionVectorEntry.Command = Second;
11534  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11535  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11536  if(Fourth == 'S')
11537  {
11538  ActionVectorEntry.SignallerControl = true;
11539  }
11540  }
11541  else if(FormatType == SNTShuttle)
11542  {
11543  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
11544  {
11545  ;
11546  }
11547  ActionVectorEntry.Command = Second;
11548  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11549  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11550  ActionVectorEntry.OtherHeadCode = Fourth;
11551  }
11552  else if(FormatType == SNSShuttle)
11553  {
11554  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
11555  {
11556  ;
11557  }
11558  ActionVectorEntry.Command = Second;
11559  ActionVectorEntry.OtherHeadCode = Third;
11560  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11561  }
11562  else if(FormatType == TimeCmdHeadCode) //fsp/rsp is TimeCmdHeadCode but may have a Fourth - see below
11563  {
11564  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
11565  {
11566  ;
11567  }
11568  ActionVectorEntry.Command = Second;
11569  ActionVectorEntry.OtherHeadCode = Third;
11570  if((Second == "fsp") || (Second == "rsp")) //new at v2.15.0 & checked in SplitEntry
11571  {
11572  ActionVectorEntry.SplitDistribution = "50-50"; //added at v2.18.0
11573  if(Fourth != "")
11574  {
11575  ActionVectorEntry.SplitDistribution = Fourth;
11576  }
11577  }
11578  }
11579  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
11580  {
11581  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
11582  {
11583  ;
11584  }
11585  ActionVectorEntry.Command = Second;
11586  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11587  }
11588  else if(FormatType == FSHNewService)
11589  {
11590  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
11591  {
11592  ;
11593  }
11594  ActionVectorEntry.Command = Second;
11595  ActionVectorEntry.OtherHeadCode = Third;
11596  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11597  }
11598  else if(FormatType == FinRemHere)
11599  {
11600  ActionVectorEntry.Command = Second;
11601  }
11602  else if(FormatType == TimeCmdDescription) //new at v2.15.0
11603  {
11604  if(CheckTimeValidity(35, First, ActionVectorEntry.EventTime))
11605  {
11606  ;
11607  }
11608  ActionVectorEntry.Command = Second;
11609  ActionVectorEntry.NewDescription = Third;
11610  }
11611  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11612  }
11613  }
11614  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
11615  {
11616  OneEntry = NewRemainder;
11617  First = "";
11618  Second = "";
11619  Third = "";
11620  Fourth = "";
11621  RearStartOrRepeatMins = 0;
11622  FrontStartOrRepeatDigits = 0;
11623  NumberOfRepeats = 0;
11624  if((FinishFlag) && (OneEntry[1] != 'R'))
11625  // already had a finish entry
11626  {
11627  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
11628  Utilities->CallLogPop(79);
11629  return(false);
11630  }
11631  if(OneEntry[1] != 'R') // must be finish
11632  {
11633  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
11634  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
11635  {
11636  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'.\nIf the program version is not the latest the "
11637  "timetable may have features that aren't compatible with the version in use.");
11638  Utilities->CallLogPop(757);
11639  return(false);
11640  }
11641  //below added at v2.14.0 to prevent unpowered trains attempting to form a new service.
11642 /* restriction removed at v2.19.0
11643  if(NewTrain && (PowerAtRail < 1) && ((Second == "Fns") || (Second == "Frh-sh") || (Second == "Fns-sh") || (Second == "F-nshs")))
11644  {
11645  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
11646  "': a train created without power can't form a new service.");
11647  Utilities->CallLogPop(2548);
11648  return(false);
11649  }
11650  //end of new additions
11651 */
11652  if(SequenceType != FinishSequence)
11653  {
11654  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
11655  Utilities->CallLogPop(785);
11656  return(false);
11657  }
11658  if(FinalCall)
11659  {
11660  // interpret & add to ActionVector
11661  TDateTime TempTime;
11662  TActionVectorEntry ActionVectorEntry;
11663  ActionVectorEntry.FormatType = FormatType;
11664  ActionVectorEntry.LocationType = LocationType;
11665  ActionVectorEntry.SequenceType = SequenceType;
11666  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
11667  ActionVectorEntry.Warning = Warning;
11668  if(FormatType == TimeCmd)
11669  {
11670  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
11671  {
11672  ;
11673  }
11674  ActionVectorEntry.Command = Second;
11675  }
11676  else if(FormatType == TimeCmdHeadCode)
11677  {
11678  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
11679  {
11680  ;
11681  }
11682  ActionVectorEntry.Command = Second;
11683  ActionVectorEntry.OtherHeadCode = Third;
11684  }
11685  else if(FormatType == FNSNonRepeatToShuttle)
11686  {
11687  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
11688  {
11689  ;
11690  }
11691  ActionVectorEntry.Command = Second;
11692  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
11693  }
11694  else if(FormatType == FSHNewService)
11695  {
11696  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
11697  {
11698  ;
11699  }
11700  ActionVectorEntry.Command = Second;
11701  ActionVectorEntry.OtherHeadCode = Third;
11702  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
11703  }
11704  else if(FormatType == ExitRailway)
11705  {
11706  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
11707  {
11708  ;
11709  }
11710  ActionVectorEntry.Command = Second;
11711  ActionVectorEntry.ExitList = ExitList;
11712  }
11713  else if(FormatType == FinRemHere)
11714  {
11715  ActionVectorEntry.Command = Second;
11716  }
11717  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11718  }
11719  }
11720  else // repeat
11721  {
11722  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
11723  {
11724  Utilities->CallLogPop(786);
11725  // error messages given in SplitRepeat
11726  return(false);
11727  }
11728  if(FinalCall)
11729  {
11730  TActionVectorEntry ActionVectorEntry;
11731  ActionVectorEntry.FormatType = Repeat;
11732  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
11733  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
11734  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
11735  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11736  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11737  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
11738  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11739  }
11740  }
11741  }
11742  }
11743  if(FinalCall)
11744  {
11745  TrainDataVector.push_back(TempTrainDataEntry);
11746  }
11747  }
11748  Utilities->CallLogPop(80);
11749  return(true);
11750 }
11751 
11752 // ---------------------------------------------------------------------------
11753 
11754 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
11755 {
11756  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
11757  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
11758  {
11759  Utilities->CallLogPop(1890);
11760  return(false);
11761  }
11762  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
11763  {
11764  Utilities->CallLogPop(1891);
11765  return(false);
11766  }
11767  Utilities->CallLogPop(1892);
11768  return(true);
11769 }
11770 
11771 // ---------------------------------------------------------------------------
11772 
11773 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
11774 // 1st 5 chars must be HH:MM, anything else will be ignored
11775 {
11776  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
11777  if(TimeStr.Length() < 5)
11778  {
11779  Utilities->CallLogPop(926);
11780  return(false);
11781  }
11782  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
11783  {
11784  Utilities->CallLogPop(927);
11785  return(false);
11786  }
11787  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
11788  {
11789  Utilities->CallLogPop(928);
11790  return(false);
11791  }
11792  if(TimeStr[3] != ':')
11793  {
11794  Utilities->CallLogPop(929);
11795  return(false);
11796  }
11797  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
11798  {
11799  Utilities->CallLogPop(930);
11800  return(false);
11801  }
11802  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
11803  {
11804  Utilities->CallLogPop(931);
11805  return(false);
11806  }
11807  while(TimeStr.Length() > 5)
11808  {
11809  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
11810  }
11811  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
11812  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
11813 
11814  if((WholeHours + FracHour) >= 95.98334)
11815  {
11816  Utilities->CallLogPop(1817);
11817  return(false); // > 95h 59m
11818  }
11819  Time = TDateTime((WholeHours + FracHour) / 24);
11820  Utilities->CallLogPop(932);
11821  return(true);
11822 }
11823 
11824 // ---------------------------------------------------------------------------
11825 
11826 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
11827  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
11828  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
11829 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
11830  Return false for failure.
11831  See description above under ProcessOneTimetableLinefor details of train action entries
11832  NB all types set except LocationType for Snt as may be located or not
11833 */{
11834  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
11835  Warning = false;
11836  TDateTime TempTime;
11837 
11838  if(OneEntry.Length() > 0)
11839  {
11840  if(OneEntry[1] == 'W') // warning
11841  {
11842  Warning = true;
11843  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
11844  // strip it off
11845  }
11846  }
11847  if(OneEntry == "Frh")
11848  {
11849  FormatType = FinRemHere;
11850  SequenceType = FinishSequence;
11851  LocationType = AtLocation;
11852  ShuttleLinkType = NotAShuttleLink;
11853  Second = "Frh";
11854  Utilities->CallLogPop(1016);
11855  return(true);
11856  }
11857  if(OneEntry.Length() < 7)
11858  {
11859  Utilities->CallLogPop(907);
11860  return(false); // 'HH:MM;' + at least a one-letter location name
11861  }
11862  int Pos = OneEntry.Pos(';'); // first segment delimiter
11863 
11864  if(Pos != 6)
11865  {
11866  Utilities->CallLogPop(908);
11867  return(false);
11868  // no delimiter or delimiter not in position 6, has to be a time so fail
11869  }
11870  First = OneEntry.SubString(1, 5); // has to be a time
11871  if(!CheckTimeValidity(16, First, TempTime))
11872  {
11873  Utilities->CallLogPop(909);
11874  return(false);
11875  }
11876  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
11877 
11878 // if((Remainder[1] >= '0') && (Remainder[1] <= '9')) changed at v2.16.0 so only 'digit-digit-colon....' interpreted as a time - to allow locations to begin with digits
11879  if((Remainder.Length() >= 3) && (Remainder[1] >= '0') && (Remainder[1] <= '9') && (Remainder[2] >= '0') && (Remainder[2] <= '9') && (Remainder[3] == ':'))
11880  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
11881  {
11882  if(Remainder.Length() < 7)
11883  {
11884  Utilities->CallLogPop(910);
11885  return(false); // 'HH:MM;' + at least a one-letter location name
11886  }
11887  Pos = Remainder.Pos(';'); // second segment delimiter
11888  if(Pos == 0)
11889  {
11890  Utilities->CallLogPop(911);
11891  return(false);
11892  // no delimiter, has to be one between departure time & location
11893  }
11894  Second = Remainder.SubString(1, 5); // has to be a time
11895  if(!CheckTimeValidity(15, Second, TempTime))
11896  {
11897  Utilities->CallLogPop(912);
11898  return(false);
11899  }
11900  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11901  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
11902  {
11903  Utilities->CallLogPop(913);
11904  return(false);
11905  }
11906  FormatType = TimeTimeLoc;
11907  SequenceType = IntermediateSequence;
11908  LocationType = AtLocation;
11909  ShuttleLinkType = NotAShuttleLink;
11910  Utilities->CallLogPop(914);
11911  return(true);
11912  }
11913  Pos = Remainder.Pos(';'); // second segment delimiter
11914  if(Pos == 0) // no third segment so second must be a location, or cdt
11915  {
11916  Second = Remainder;
11917  if(Second == "cdt")
11918  {
11919  FormatType = TimeCmd;
11920  ShuttleLinkType = NotAShuttleLink;
11921  LocationType = AtLocation;
11922  SequenceType = IntermediateSequence;
11923  Utilities->CallLogPop(915);
11924  return(true);
11925  }
11926  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
11927  {
11928  Utilities->CallLogPop(916);
11929  return(false);
11930  }
11931  else
11932  {
11933  FormatType = TimeLoc;
11934  LocationType = AtLocation;
11935  SequenceType = IntermediateSequence;
11936  ShuttleLinkType = NotAShuttleLink;
11937  Utilities->CallLogPop(917);
11938  return(true);
11939  }
11940  }
11941  // here if second segment is a command, with a third & maybe fourth segments
11942  if((Pos != 4) && (Pos != 7) && (Pos != 8))
11943  {
11944  Utilities->CallLogPop(918);
11945  return(false);
11946  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
11947  }
11948  Second = Remainder.SubString(1, Pos - 1); // command
11949 
11950  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11951  // details
11952  Pos = Remainder.Pos(';'); // third segment delimiter
11953  if(Pos == 0)
11954  {
11955  Third = Remainder;
11956  }
11957  else
11958  {
11959  Third = Remainder.SubString(1, Pos - 1);
11960  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11961  }
11962 
11963  if((Second == "Snt") || (Second == "Snt-sh"))
11964  // third has to be 2 element idents with a space between
11965  {
11966  int SpacePos = Third.Pos(' ');
11967  if(SpacePos == 0)
11968  {
11969  Utilities->CallLogPop(919);
11970  return(false); // no space
11971  }
11972  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
11973  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
11974  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
11975  if(CheckLocationsExistInRailway)
11976  {
11977  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
11978  {
11979  Utilities->CallLogPop(920);
11980  return(false);
11981  }
11982  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
11983  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
11984  }
11985  if(Second == "Snt")
11986  {
11987  FormatType = StartNew;
11988  SequenceType = StartSequence;
11989  LocationType = NoLocation;
11990  // can't be set until know whether located or not - done in SecondPassActions
11991  ShuttleLinkType = NotAShuttleLink;
11992  }
11993  else // Snt-sh
11994  {
11995  FormatType = SNTShuttle;
11996  LocationType = AtLocation;
11997  SequenceType = StartSequence;
11998  ShuttleLinkType = ShuttleLink;
11999  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
12000  {
12001  Utilities->CallLogPop(1038);
12002  return(false);
12003  }
12004  }
12005  Utilities->CallLogPop(921);
12006  return(true);
12007  }
12008  if(Second == "Sns-sh") // third & fourth have to be headcodes
12009  {
12010  FormatType = SNSShuttle;
12011  LocationType = AtLocation;
12012  SequenceType = StartSequence;
12013  ShuttleLinkType = ShuttleLink;
12014  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
12015  {
12016  Utilities->CallLogPop(1039);
12017  return(false);
12018  }
12019  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
12020  {
12021  Utilities->CallLogPop(1040);
12022  return(false);
12023  }
12024  Utilities->CallLogPop(1041);
12025  return(true);
12026  }
12027  if(Second == "F-nshs")
12028  {
12029  FormatType = FNSNonRepeatToShuttle;
12030  LocationType = AtLocation;
12031  SequenceType = FinishSequence;
12032  ShuttleLinkType = ShuttleLink;
12033  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
12034  {
12035  Utilities->CallLogPop(1047);
12036  return(false);
12037  }
12038  Utilities->CallLogPop(1048);
12039  return(true);
12040  }
12041  if(Second == "Sns-fsh")
12042  {
12043  FormatType = SNSNonRepeatFromShuttle;
12044  LocationType = AtLocation;
12045  SequenceType = StartSequence;
12046  ShuttleLinkType = ShuttleLink;
12047  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
12048  {
12049  Utilities->CallLogPop(1098);
12050  return(false);
12051  }
12052  Utilities->CallLogPop(1099);
12053  return(true);
12054  }
12055  if(Second == "Fns-sh") // third & fourth have to be headcodes
12056  {
12057  FormatType = FSHNewService;
12058  LocationType = AtLocation;
12059  SequenceType = FinishSequence;
12060  ShuttleLinkType = ShuttleLink;
12061  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
12062  {
12063  Utilities->CallLogPop(1050);
12064  return(false);
12065  }
12066  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
12067  {
12068  Utilities->CallLogPop(1051);
12069  return(false);
12070  }
12071  Utilities->CallLogPop(1052);
12072  return(true);
12073  }
12074  // new segment for 'pas'
12075  if(Second == "pas") // third has to be a location
12076  {
12077  FormatType = PassTime;
12078  LocationType = EnRoute;
12079  SequenceType = IntermediateSequence;
12080  ShuttleLinkType = NotAShuttleLink;
12081  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
12082  {
12083  Utilities->CallLogPop(1515);
12084  return(false);
12085  }
12086  Utilities->CallLogPop(1516);
12087  return(true);
12088  }
12089  // new segment for revised 'Fer'
12090  if(Second == "Fer")
12091  // third has to be a set of IDs separated by spaces, and at least 1
12092  {
12093  FormatType = ExitRailway;
12094  LocationType = EnRoute;
12095  SequenceType = FinishSequence;
12096  ShuttleLinkType = NotAShuttleLink;
12097  if(CheckLocationsExistInRailway)
12098  {
12099  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
12100  {
12101  Utilities->CallLogPop(1519);
12102  return(false);
12103  }
12104  }
12105  Utilities->CallLogPop(1520);
12106  return(true);
12107  }
12108  if(Second == "dsc") //new at v2.15.0 - change description
12109  {
12110  if(Third.Length() > 60)
12111  {
12112  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters in '" + Third + "'");
12113  Utilities->CallLogPop(2582);
12114  return(false);
12115  }
12116  for(int x = 1; x < Third.Length() + 1; x++)
12117  {
12118 // if((Third[x] < ' ') || (Third[x] > '~')) changed at v2.16.0 to allow extended characters in location names
12119  if((Third[x] < ' ') && (Third[x] >= 0))
12120  {
12121  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + Third + "'");
12122  Utilities->CallLogPop(2583);
12123  return(false);
12124  }
12125  }
12126  FormatType = TimeCmdDescription;
12127  LocationType = AtLocation;
12128  SequenceType = IntermediateSequence;
12129  ShuttleLinkType = NotAShuttleLink;
12130  Utilities->CallLogPop(2604);
12131  return(true);
12132  }
12133 
12134 // if((Second == "fsp") || (Second == "fsp")) then there can optionlly be a fourth: xx-yy where xx = percentage mass & yy = percentage power in the split train
12135 
12136  // all remainder must be TimeCmdHeadCode types to be valid
12137  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
12138  (Second != "Frh-sh"))
12139  {
12140  Utilities->CallLogPop(922);
12141  return(false); // all TimeCmdHeadCode types
12142  }
12143  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
12144  {
12145  Utilities->CallLogPop(923);
12146  return(false);
12147  }
12148  FormatType = TimeCmdHeadCode;
12149  LocationType = AtLocation;
12150  if(Second == "Frh-sh")
12151  {
12152  ShuttleLinkType = ShuttleLink;
12153  }
12154  else
12155  {
12156  ShuttleLinkType = NotAShuttleLink;
12157  }
12158  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
12159  {
12160  SequenceType = FinishSequence;
12161  }
12162  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
12163  {
12164  SequenceType = IntermediateSequence;
12165  }
12166  if((Second == "Sfs") || (Second == "Sns"))
12167  {
12168  SequenceType = StartSequence;
12169  }
12170  //new at v2.15.0 to allow splits to have different characteristics - Fourth specifies: AA-BB where AA & BB can be 1 or 2 digits, AA is percentage mass
12171  if((Fourth != "") && ((Second == "fsp") || (Second == "rsp"))) //& BB is percentage power allocated to the split off train
12172  {
12173  if(!CheckFourthValidityForSplit(Fourth, GiveMessages))
12174  {
12175  Utilities->CallLogPop(2584);
12176  return(false);
12177  }
12178  }
12179  Utilities->CallLogPop(924);
12180  return(true);
12181 }
12182 
12183 // ---------------------------------------------------------------------------
12184 
12185 bool TTrainController::CheckFourthValidityForSplit(AnsiString SplitDistributionString, bool GiveMessages) //new at v2.15.0
12186 {
12187  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",CheckFourthValidityForSplit," + SplitDistributionString);
12188  bool ErrorFlag = false;
12189  int x, y;
12190  if((SplitDistributionString.Length() > 6 ) || (SplitDistributionString.Length() < 3))
12191  {
12192  ErrorFlag = true;
12193  }
12194  int pos = SplitDistributionString.Pos('-');
12195  if(pos == 0)
12196  {
12197  ErrorFlag = true;
12198  }
12199  else
12200  {
12201  AnsiString MassStr = SplitDistributionString.SubString(1, pos - 1);
12202  AnsiString PowerStr = SplitDistributionString.SubString(pos + 1, SplitDistributionString.Length() - pos);
12203  try //allows for one or two digit percentages
12204  {
12205  int x = MassStr.ToInt();
12206  int y = PowerStr.ToInt();
12207  if((x > 99) || (x < 1) || (y > 100) || (y < 0))
12208  {
12209  ErrorFlag = true;
12210  }
12211  }
12212  catch(const Exception &e) //non-error catch
12213  {
12214  ErrorFlag = true;
12215  }
12216  }
12217  if(ErrorFlag)
12218  {
12219  TimetableMessage(GiveMessages, "Error in split distribution " + SplitDistributionString + ", should be 'AA-BB' where AA is the percentage mass (min 1, max 99) and BB the percentage " +
12220  "power for the new split-off train");
12221  Utilities->CallLogPop(2585);
12222  return(false);
12223  }
12224  Utilities->CallLogPop(2601);
12225  return(true);
12226 }
12227 
12228 // ---------------------------------------------------------------------------
12229 
12230 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
12231 {
12232  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with 'digit-digit-colon'
12233  // and contains no control characters changed at v2.16.0 to allow extended characters in location names
12234  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
12235  if(LocStr == "")
12236  {
12237  Utilities->CallLogPop(1353);
12238  return(false); // has to have at least one character
12239  }
12240 // if((LocStr[1] >= '0') && (LocStr[1] <= '9')) //changed at v2.16.0 to allow locations to begine with digits, if 'digit-digit-colon' then must be a time
12241  if((LocStr.Length() >= 3) && (LocStr[1] >= '0') && (LocStr[1] <= '9') && (LocStr[2] >= '0') && (LocStr[2] <= '9') && (LocStr[3] == ':'))
12242  {
12243  Utilities->CallLogPop(1354);
12244  return(false); // can't begin with 'digit-digit-colon' as this regarded as a time
12245  }
12246  for(int x = 1; x < LocStr.Length() + 1; x++)
12247  {
12248  if(((LocStr[x] < ' ') && (LocStr[x] >= 0)) || (LocStr[x] == ',') || (LocStr[x] == ';')) //changed at v2.16.0 to allow extended characters in location names
12249  {
12250  Utilities->CallLogPop(1355);
12251  return(false); // contains a special character or ',' or ';'
12252  }
12253 /*
12254  if(LocStr[x] > 'z') //dropped at v2.16.0 to allow extended characters in location names
12255  {
12256  Utilities->CallLogPop(1356);
12257  return(false); // contains a character outside the standard ASCII set
12258  }
12259 */
12260  }
12261  // check exists in railway location list if CheckLocationsExistInRailway is true
12262  if(CheckLocationsExistInRailway)
12263  {
12264  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
12265  {
12266  TimetableMessage(GiveMessages, "Location name '" + LocStr +
12267  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
12268  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
12269  "that includes a continuation will not be valid.");
12270  Utilities->CallLogPop(1357);
12271  return(false);
12272  }
12273  }
12274  Utilities->CallLogPop(1358);
12275  return(true);
12276 }
12277 
12278 // ---------------------------------------------------------------------------
12279 
12280 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
12281 {
12282  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
12283  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
12284  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
12285  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
12286  HeadCode);
12287  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
12288  {
12289  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
12290  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
12291  Utilities->CallLogPop(1359);
12292  return(false);
12293  }
12294  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
12295  for(int x = 1; x < (HeadCode.Length() + 1); x++)
12296  {
12297  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
12298  {
12299  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
12300  Utilities->CallLogPop(1895);
12301  return(false);
12302  }
12303  }
12304  // secondly ensure the true Headcode only has letters or digits
12305  for(int x = 3; x >= 0; x--)
12306  {
12307  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
12308  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
12309  {
12310  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
12311  Utilities->CallLogPop(1790);
12312  return(false);
12313  }
12314  }
12315  Utilities->CallLogPop(1364);
12316  return(true);
12317 }
12318 
12319 // ---------------------------------------------------------------------------
12320 
12321 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
12322 // set of track element IDs, separated by spaces, and at least 1 present
12323 {
12324  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndPopulateListOfIDs," + IDSet); //had wrong title, changed at v2.13.0
12325  ExitList.clear();
12326  AnsiString CurrentID = "";
12327 
12328  if(IDSet.Length() == 0)
12329  {
12330  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
12331  Utilities->CallLogPop(1521);
12332  return(false);
12333  }
12334  for(int x = 1; x <= IDSet.Length(); x++)
12335  {
12336  char C = IDSet[x];
12337  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
12338  {
12339  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
12340  Utilities->CallLogPop(1522);
12341  return(false);
12342  }
12343 /* don't use, error checks in GetTrackVectorPositionFromString instead
12344  if(C == '-') //this section added at v2.13.0 because of Amon Sadler's error file submitted 24/03/22
12345  {
12346  if((x==1) || (x == IDSet.Length()))
12347  {
12348  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12349  Utilities->CallLogPop(2479);
12350  return(false);
12351  }
12352  if((IDSet[x-1] < '0') || (IDSet[x-1] > '9') || (IDSet[x+1] < '0') || (IDSet[x+1] > '9'))
12353  {
12354  TimetableMessage(GiveMessages, "Illegal minus character ('-') in the set of element IDs following 'Fer' in '" + IDSet + "'");
12355  Utilities->CallLogPop(2480);
12356  return(false);
12357  }
12358  }
12359 */
12360  }
12361  int Pos = IDSet.Pos(' '); // look for the first space
12362 
12363  while(true)
12364  {
12365  if(Pos == 0)
12366  {
12367  CurrentID = IDSet;
12368  IDSet = "";
12369  }
12370  else
12371  {
12372  CurrentID = IDSet.SubString(1, Pos - 1);
12373  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
12374  }
12375  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
12376  if(VecPos == -1)
12377  {
12378  Utilities->CallLogPop(1523);
12379  return(false); // messages given in GetTrackVectorPositionFromString
12380  }
12381  else
12382  {
12383  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
12384  {
12385  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
12386  Utilities->CallLogPop(1524);
12387  return(false);
12388  }
12389  else
12390  {
12391  // first check for duplicates
12392  if(!ExitList.empty())
12393  {
12394  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
12395  {
12396  if(*ELIT == VecPos)
12397  {
12398  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
12399  Utilities->CallLogPop(1532);
12400  return(false);
12401  }
12402  }
12403  }
12404  // of OK add it to the list
12405  ExitList.push_back(VecPos);
12406  }
12407  }
12408  if(IDSet == "")
12409  {
12410  Utilities->CallLogPop(1525);
12411  return(true);
12412  }
12413  else
12414  {
12415  Pos = IDSet.Pos(' '); // look for the next space
12416  }
12417  } // while(true)
12418 }
12419 
12420 // ---------------------------------------------------------------------------
12421 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
12422  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
12423 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
12424 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
12425 // of each item
12426 {
12427  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
12428  int Pos = 0;
12429  AnsiString Remainder = "";
12430  int SemiColonCount = 0;
12431 
12432  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
12433  {
12434  if(TrainInfoStr[x] == ';')
12435  {
12436  SemiColonCount++;
12437  }
12438  }
12439  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
12440  {
12441  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
12442  "'. Should be headcode + optional description for a continuing service;" +
12443  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
12444  Utilities->CallLogPop(880);
12445  return(false);
12446  }
12447  if(SemiColonCount == 0)
12448  {
12449  HeadCode = TrainInfoStr;
12450  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
12451  {
12452  Utilities->CallLogPop(881);
12453  return(false);
12454  }
12455  Utilities->CallLogPop(882);
12456  return(true);
12457  }
12458  if(SemiColonCount == 1) // headcode & description only
12459  {
12460  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12461  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12462  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12463  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
12464  {
12465  Utilities->CallLogPop(883);
12466  return(false);
12467  }
12468  if(Description == "")
12469  {
12470  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12471  Utilities->CallLogPop(884);
12472  return(false);
12473  }
12474  if(Description.Length() > 60)
12475  {
12476  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12477  Utilities->CallLogPop(1157);
12478  return(false);
12479  }
12480  for(int x = 1; x < Description.Length() + 1; x++)
12481  {
12482 // if((Description[x] < ' ') || (Description[x] > '~')) changed at v2.16.0 to allow extended characters in location names
12483  if((Description[x] < ' ') && (Description[x] >= 0))
12484  {
12485  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12486  Utilities->CallLogPop(885);
12487  return(false);
12488  }
12489  }
12490  Utilities->CallLogPop(886);
12491  return(true);
12492  }
12493  // if here must have 6 or 7 semicolons
12494  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
12495  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
12496  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
12497  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
12498  {
12499  Utilities->CallLogPop(887);
12500  return(false);
12501  }
12502  Pos = Remainder.Pos(';'); // 2nd delimiter
12503  Description = Remainder.SubString(1, Pos - 1);
12504  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12505  if(Description == "")
12506  {
12507  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
12508  Utilities->CallLogPop(888);
12509  return(false);
12510  }
12511  if(Description.Length() > 60)
12512  {
12513  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
12514  Utilities->CallLogPop(1158);
12515  return(false);
12516  }
12517  for(int x = 1; x < Description.Length() + 1; x++)
12518  {
12519 // if((Description[x] < ' ') || (Description[x] > 126)) changed at v2.16.0 to allow extended characters in location names
12520  if((Description[x] < ' ') && (Description[x] >= 0))
12521  {
12522  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
12523  Utilities->CallLogPop(889);
12524  return(false);
12525  }
12526  }
12527  Pos = Remainder.Pos(';'); // 3rd delimiter
12528  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
12529 
12530  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12531  if(StartSpeedStr == "")
12532  {
12533  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
12534  Utilities->CallLogPop(890);
12535  return(false);
12536  }
12537  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
12538  {
12539  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
12540  {
12541  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
12542  Utilities->CallLogPop(891);
12543  return(false);
12544  }
12545  }
12546  StartSpeed = StartSpeedStr.ToInt();
12547  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12548  {
12549  StartSpeed = TTrain::MaximumSpeedLimit;
12550  if(!SSHigh) // added at v2.4.0
12551  {
12552  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12553  SSHigh = true;
12554  }
12555  }
12556  Pos = Remainder.Pos(';'); // 4th delimiter
12557  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
12558 
12559  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12560  if(MaxRunningSpeedStr == "")
12561  {
12562  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
12563  Utilities->CallLogPop(892);
12564  return(false);
12565  }
12566  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
12567  {
12568  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
12569  {
12570  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
12571  Utilities->CallLogPop(893);
12572  return(false);
12573  }
12574  }
12575  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
12576  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
12577  {
12578  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
12579  if(!MRSHigh) // added at v2.4.0
12580  {
12581  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12582  MRSHigh = true;
12583  }
12584  }
12585  if(MaxRunningSpeed < 10)
12586  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12587  {
12588  MaxRunningSpeed = 10;
12589  if(!MRSLow) // added at v2.4.0
12590  {
12591  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12592  MRSLow = true;
12593  }
12594  }
12595  Pos = Remainder.Pos(';'); // 5th delimiter
12596  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
12597 
12598  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12599  if(MassStr == "")
12600  {
12601  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
12602  Utilities->CallLogPop(895);
12603  return(false);
12604  }
12605  for(int x = 1; x < MassStr.Length() + 1; x++)
12606  {
12607  if((MassStr[x] < '0') || (MassStr[x] > '9'))
12608  {
12609  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
12610  Utilities->CallLogPop(896);
12611  return(false);
12612  }
12613  }
12614  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
12615  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
12616  {
12617  Mass = TTrain::MaximumMassLimit;
12618  if(!MassHigh) // added at v2.4.0
12619  {
12620  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
12621  MassHigh = true;
12622  }
12623  }
12624  if(Mass == 0)
12625  {
12626  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
12627  Utilities->CallLogPop(897);
12628  return(false);
12629  }
12630  Pos = Remainder.Pos(';'); // 6th delimiter
12631  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
12632 
12633  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12634  if(MaxBrakeForceStr == "")
12635  {
12636  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
12637  Utilities->CallLogPop(898);
12638  return(false);
12639  }
12640  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
12641  {
12642  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
12643  {
12644  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
12645  Utilities->CallLogPop(899);
12646  return(false);
12647  }
12648  }
12649  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
12650 
12651  // convert to kg force
12652  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
12653  {
12654  MaxBrakeForce = Mass;
12655  if(!BFHigh) // added at v2.4.0
12656  {
12657  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
12658  BFHigh = true;
12659  }
12660  }
12661  if((MaxBrakeForce / Mass) < 0.01)
12662  {
12663  MaxBrakeForce = Mass * 0.01;
12664  if(!BFLow) // added at v2.4.0
12665  {
12666  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
12667  BFLow = true;
12668  }
12669  }
12670  // convert to m/s/s
12671  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
12672  // now may have just a power entry or power and signaller max. speed
12673  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
12674 
12675  if(SemiColonCount == 6)
12676  {
12677  GrossPowerStr = Remainder;
12678  SignallerSpeedStr = "30"; // default value
12679  }
12680  else // must be 7
12681  {
12682  Pos = Remainder.Pos(';'); // 7th delimiter
12683  GrossPowerStr = Remainder.SubString(1, Pos - 1);
12684  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12685  }
12686  // deal with GrossPower
12687  if(GrossPowerStr == "")
12688  {
12689  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
12690  Utilities->CallLogPop(901);
12691  return(false);
12692  }
12693  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
12694  {
12695  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
12696  {
12697  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
12698  Utilities->CallLogPop(902);
12699  return(false);
12700  }
12701  }
12702 
12703  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
12704 
12705  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
12706  {
12707  GrossPower = TTrain::MaximumPowerLimit;
12708  if(!PwrHigh)
12709  {
12710  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
12711  PwrHigh = true;
12712  }
12713  }
12714  else if(GrossPower == 0) // changed at v2.4.0
12715  {
12716  GrossPower = 0.1;
12717  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
12718  }
12719  else if((GrossPower > 0) && (GrossPower < 10000))
12720  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
12721  {
12722  GrossPower = 10000;
12723  }
12724  PowerAtRail = GrossPower * 0.8;
12725  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
12726 
12727  // deal with SignallerSpeed
12728  if(SignallerSpeedStr == "")
12729  {
12730  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
12731  Utilities->CallLogPop(1771);
12732  return(false);
12733  }
12734  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
12735  {
12736  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
12737  {
12738  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
12739  Utilities->CallLogPop(1769);
12740  return(false);
12741  }
12742  }
12743  SignallerSpeed = SignallerSpeedStr.ToInt();
12744  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
12745  {
12746  SignallerSpeed = TTrain::MaximumSpeedLimit;
12747  if(!SigSHigh)
12748  {
12749  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
12750  SigSHigh = true;
12751  }
12752  }
12753  if(SignallerSpeed < 10)
12754  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
12755  {
12756  SignallerSpeed = 10;
12757  if(!SigSLow)
12758  {
12759  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
12760  SigSLow = true;
12761  }
12762  // Utilities->CallLogPop(1770);
12763  // return false;
12764  }
12765  Utilities->CallLogPop(904);
12766  return(true);
12767 }
12768 
12769 // ---------------------------------------------------------------------------
12770 
12771 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
12772  bool GiveMessages)
12773 {
12774  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
12775  // function checks validity of each item and returns false for error
12776  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
12777  if(OneEntry.Length() < 7)
12778  {
12779  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12780  Utilities->CallLogPop(865);
12781  return(false);
12782  }
12783  int SemiColonCount = 0;
12784 
12785  for(int x = 1; x < OneEntry.Length() + 1; x++)
12786  {
12787  if(OneEntry[x] == ';')
12788  {
12789  SemiColonCount++;
12790  }
12791  }
12792  if(SemiColonCount != 3)
12793  {
12794  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12795  Utilities->CallLogPop(866);
12796  return(false);
12797  }
12798  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
12799  {
12800  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
12801  Utilities->CallLogPop(867);
12802  return(false);
12803  }
12804  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
12805  // strip off R;
12806 
12807  int Pos = 0;
12808 
12809  Pos = Remainder.Pos(';');
12810  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
12811 
12812  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12813  if(MinutesStr == "")
12814  {
12815  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
12816  Utilities->CallLogPop(868);
12817  return(false);
12818  }
12819  if(MinutesStr.Length() > 3)
12820  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
12821  {
12822  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
12823  Utilities->CallLogPop(2119);
12824  return(false);
12825  }
12826  for(int x = 1; x < MinutesStr.Length() + 1; x++)
12827  {
12828  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
12829  {
12830  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
12831  Utilities->CallLogPop(869);
12832  return(false);
12833  }
12834  }
12835  RearStartOrRepeatMins = MinutesStr.ToInt();
12836  if(RearStartOrRepeatMins == 0)
12837  {
12838  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
12839  Utilities->CallLogPop(870);
12840  return(false);
12841  }
12842  Pos = Remainder.Pos(';');
12843  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
12844 
12845  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12846  if(DigitsStr == "")
12847  {
12848  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
12849  Utilities->CallLogPop(871);
12850  return(false);
12851  }
12852  for(int x = 1; x < DigitsStr.Length() + 1; x++)
12853  {
12854  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
12855  {
12856  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
12857  Utilities->CallLogPop(872);
12858  return(false);
12859  }
12860  }
12861  if(DigitsStr.Length() > 2)
12862  {
12863  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
12864  Utilities->CallLogPop(873);
12865  return(false);
12866  }
12867  FrontStartOrRepeatDigits = DigitsStr.ToInt();
12868 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
12869  route rather than the service
12870  if(FrontStartOrRepeatDigits == 0)
12871  {
12872  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
12873  Utilities->CallLogPop(874);
12874  return false;
12875  }
12876 */
12877  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
12878  // new for v0.6b for unrestricted headcodes
12879  {
12880  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
12881  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
12882  Utilities->CallLogPop(1889);
12883  return(false);
12884  }
12885  AnsiString NumberStr = Remainder;
12886 
12887  if(NumberStr == "")
12888  {
12889  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
12890  Utilities->CallLogPop(875);
12891  return(false);
12892  }
12893  if(NumberStr.Length() > 4)
12894  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
12895  {
12896  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
12897  Utilities->CallLogPop(2118);
12898  return(false);
12899  }
12900  for(int x = 1; x < NumberStr.Length() + 1; x++)
12901  {
12902  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
12903  // catches negative numbers
12904  {
12905  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
12906  Utilities->CallLogPop(876);
12907  return(false);
12908  }
12909  }
12910  NumberOfRepeats = NumberStr.ToInt();
12911  if(NumberOfRepeats == 0)
12912  {
12913  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
12914  Utilities->CallLogPop(877);
12915  return(false);
12916  }
12917  Utilities->CallLogPop(878);
12918  return(true);
12919 }
12920 
12921 // ---------------------------------------------------------------------------
12922 
12923 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
12924 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
12925  vector rather than the timetable
12926  Note also that for unlocated Snt entries the LocationType hasn't yet been set
12927 
12928  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
12929 
12930 Updated significantly for v2.15.0. Current procedure:-
12931 
12932 Preliminary checks for v0.2b without changing anything, carry each out separately:-
12933  1) must have at least one actionvector entry
12934  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
12935  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
12936  4) first entry must be a start;
12937  4a) if first entry is Snt and second is a finish then it can't be Fns-sh or Frh-sh
12938  4b) if first entry is Sns or Sfs and second is a finish then it must be either Frh or Fjo
12939  4c) if first entry is Snt-sh, Sns-sh or Sns-fsh second can't be a finish
12940  5) a start must be the first entry;
12941  6) a repeat entry must be the last;
12942  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
12943  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
12944  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
12945 
12946 Set location for located Snt or Snt-sh and ensure successor AtLocation
12947 For unlocated Snt-sh give error message
12948 For unlocated Snt & not sig control check successor moving
12949 
12950 Check all other starts (all located) have valid successors
12951 
12952 Set location for Sns-sh and Sns-fsh from following TimeLoc, if not one then give message
12953 
12954 Carry out linkage checks to ensure all links present, no data set yet & locations not checked yet. First check for duplicates, then for cross
12955 references, then for non-repeating shuttle cross refs. This is done because the later location naming functions give error messages if there
12956 are missing links.
12957 
12958 Set names for all Fns finishes from earlier named event or fail if can't find
12959 Set names for linked Sns events with same event times from above, but first carry out immediate successor checks and give error message for:-
12960 no successors, moving successor, another start sequence, a finish that isn't Frh or Fjo or a repeat. No error messages given here for location
12961 not found, that check done later.
12962 
12963 Trap errors where rsp/fsp follows an Sfs without a TimeLoc arrival before (or unlikely to be able to set fsp/rsp/Sfs location because Sfs locs set
12964 from linked fsp/rsp events)
12965 
12966 Name all fsp/rsp events, then check that all named or give error message.
12967 
12968 Set all Sfs names from above fsp/rsp links with same event times, but first carry out immediate successor checks and give error message for:-
12969 no successors, moving successor, another start sequence, a finish that isn't Frh or Fjo or a repeat. No error messages given here for location
12970 not found, that check done later.
12971 
12972 Set remaining AtLoc Command locations from preceding named event
12973 
12974 All location names should now be set
12975 
12976 Final detailed check of names for all AtLoc Commands. If find any without a name give an error message:-
12977 If jbo, fsp, rsp, cdt or dsc say must be preceded by a named event at same location, normally an arrival
12978 If Sns or Sfs say to make sure the linked finish event is preceded by a named event at same location, normally an arrival
12979 If Snt-sh say to make sure that the service starts with zero speed and is at a named location
12980 If Sns-fsh or Sns-sh say to make sure that the event is followed (not necessarily immediately) by a departure
12981 If Frh, Fns, Fjo, Frh-sh, Fns-sh or F-nshs say that the event must be preceded by an event at the same location that has an identified location name,
12982 normally an arrival.
12983 Missing: pas & Fer not AtLoc, Snt whether located or not covered in detail earlier.
12984 
12985 Later checks as before 2.15.0 changes:-
12986 
12987 Check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12988 
12989 Set arrival & departure times for TimeLocs & set their EventTimes to -1 (up to now all have times as EventTime)
12990 
12991 Perform remaining successor checks for TimeLocs
12992 
12993 Check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1, & repeats have no times
12994 set
12995 
12996 Check times stay same or increase through a service, note that can have time of 0 if include midnight
12997 
12998 Check locations consistent
12999 
13000 Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0) i.e.
13001 same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0. Message
13002 given in InterfaceUnit
13003 
13004 Check all locations except unlocated 'Snt' & 'Fer' have LocationName set and throw error if not.
13005 
13006 Carry out full cross reference and duplicate link checks for all services inc shuttles, and set data and check location consistency
13007 
13008 Check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13009 when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles, don't ever need to and as
13010 designed would skip repeats
13011 
13012 Check all entries have all types set to something and throw error if not
13013 
13014 All OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
13015 
13016 Check that don't include any Continuation names
13017 
13018 Check that all repeat times below 96h
13019 
13020 Now that all set up change any extended headcodes back to ordinary headcodes (had been service references until now.
13021 
13022 Finally call BuildContinuationTrainExpectationMultiMap
13023 
13024 ***********************************
13025 
13026 For info:-
13027 class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
13028 {
13029 public:
13030 AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
13032 bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
13033 bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
13034 int NumberOfRepeats; ///< the number of repeating services
13035 int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
13037 TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
13039 TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
13040 TTimetableFormatType FormatType; ///< defines the timetable action type
13041 TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
13042 TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
13043 TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
13044 TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
13046 TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
13048 
13049 // inline function
13050 
13052 TActionVectorEntry() {
13053 RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
13054 SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
13055 ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
13056 Warning = false; SignallerControl = false;
13057 }
13058 };
13059 
13060 typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
13061 
13062 class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
13063 {
13064 public:
13065 AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
13068 double MaxBrakeRate; ///< in metres/sec/sec
13069 double MaxRunningSpeed; ///< in km/h
13070 double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
13071 int Mass; ///< in kg
13072 int NumberOfTrains; ///< number of repeats + 1
13073 int SignallerSpeed; ///< in km/h for use when under signaller control
13074 int StartSpeed; ///< in km/h
13075 TActionVector ActionVector; ///< all the actions for the train
13076 TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
13077 
13078 //inline function
13079 
13081 TTrainDataEntry()
13082 {
13083 StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
13084 }
13085 };
13086 
13087 Allowable successors:-
13088 Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
13089 Snt located -> No starts, no finishes except Frh, Fjo (as of v2.0.0), Fns, and F-nshs, no repeat, pas, TimeTimeLoc or TimeLoc arr;
13090 any other cmd or TimeLoc (dep) OK
13091 Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
13092 Sfs -> No starts, finishes except Frh & Fjo (as of v2.15.0), repeats, pas, TimeTimeLoc, TimeLoc arr, rsp, fsp; any other cmd or
13093 TimeLoc (dep) OK [must have departure & arrival before another split]
13094 Sns -> No starts, finishes except Frh & Fjo (as of v2.15.0), repeats, pas, TimeTimeLoc or TimeLoc arr; any other cmd or TimeLoc (dep) OK
13095 Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in
13096 sequence to
13097 set location, else fails)
13098 Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in
13099 sequence to
13100 set location, else fails)
13101 Fns -> R only [must be preceded by a TimeLoc arrival at the finish location, not necessarily immediately]
13102 F-nshs -> Nothing (no repeats permitted)
13103 Fjo -> R only
13104 Frh -> R only
13105 Fer -> R only
13106 Frh-sh -> R only
13107 Fns-sh -> R only
13108 jbo -> No starts, repeats, pas, Fer or TimeTimeLoc; TimeLoc (dep), others OK [must be preceded by an event whose location is set]
13109 fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13110 rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13111 cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13112 dsc -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK [must be preceded by an event whose location is set]
13113 TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
13114 TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13115 TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13116 (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
13117 Repeat -> Nothing
13118 
13119 There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
13120 Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
13121 is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
13122 If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
13123 Check all times increase or stay same through ActionVector
13124 Cycle through all entries in vector setting arr & dep times based on above list
13125 Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
13126 Check locations match the arr & dep TimeLoc entries
13127 Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
13128 Make above valid succession checks
13129 Check all splits have matching Sfs headcodes (both ways), add locations to Sfs's & check times same [Sfs loc derived from preceding fsp/rsp
13130 loc]
13131 Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to Sns's & check times same [Sns loc derived from
13132 preceding Fns loc]
13133 Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13134 Check each Fns has matching Sns headcodes (both ways), add locations to Fns's & check times same
13135 Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
13136 Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13137 Set train info for Sfs & Sns entries
13138 Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
13139 Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
13140 element at each end, or length of 3 & 1 extra element at either end
13141 Check all TimeLocs have either Arr or Dep times set and EventTime == -1
13142 Check all Cmds have EventTime set & Arr & Dep times = -1
13143 Check all locations except unlocated Snts, Fers and Repeats have a LocationName
13144 
13145 Give messages in function if errors detected and clear the vector. Return false for failure.
13146 */
13147 
13148 /* Earlier checks:-
13149 Checks carried out with error messages in this function:-
13150 At least one comma in the line (it's based on a csv file);
13151 No entries following train information;
13152 At least one comma in remainder after train information (i.e at least a start and a finish entry);
13153 SplitEntry returns false in an intermediate entry - message repeats the entry for information;
13154 First entry not a start entry;
13155 Train information incomplete before a start entry;
13156 Entry follows a finish entry but doesn't begin with 'R';
13157 SplitEntry returns false in a finish entry - message repeats the entry for information;
13158 Last action entry isn't a finish entry.
13159 
13160 Function returns false with no message if:-
13161 Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
13162 time is found at all then an error message is given in the calling function);
13163 SplitTrainInfo returns false (message given in called function);
13164 SplitRepeat returns false (message given in called function).
13165 
13166 Double crosslink (shuttle) table: [OtherHeadCode, NonRepeatingShuttleLinkHeadCode, LinkedTrainEntryPtr, NonRepeatingShuttleLinkEntryPtr] <-- these for
13167 easier searching for this table
13168 
13169 Command Format OtherHead NonRepeating- LinkedTrain- NonRepeating- Decsription
13170  Code ShuttleLink- EntryPtr ShuttleLink
13171  HeadCode EntryPtr
13172 
13173 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
13174 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
13175 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
13176 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (fdr-shld be rtn)N (shld be fdr) Luckily NonRep link not needed
13177 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
13178 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
13179 
13180 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
13181 
13182 */
13183 {
13184  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
13185  if(TrainDataVector.empty())
13186  {
13187  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
13188  TrainDataVector.clear();
13189  Utilities->CallLogPop(1832);
13190  return(false);
13191  }
13192 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
13193  1) must have at least one actionvector entry
13194  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
13195  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
13196  4) first entry must be a start;
13197  4a) if first entry is Snt and second is a finish then it can't be Fns-sh or Frh-sh
13198  4b) if first entry is Sns or Sfs and second is a finish then it must be either Frh or Fjo
13199  4c) if first entry is Snt-sh, Sns-sh or Sns-fsh second can't be a finish
13200  5) a start must be the first entry;
13201  6) a repeat entry must be the last;
13202  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
13203  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
13204  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
13205 */
13206 
13207  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
13208  TwoLocationFlag = false; //added at v2.9.1
13209  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
13210  {
13211  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13212  if(TrainDataVector.at(x).ActionVector.empty())
13213  {
13214  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
13215  TrainDataVector.clear();
13216  Utilities->CallLogPop(1833);
13217  return(false);
13218  }
13219  }
13220  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
13221  {
13222  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13223  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13224  if(!(AVEntry0.SignallerControl))
13225  {
13226  if(TrainDataVector.at(x).ActionVector.size() == 1)
13227  {
13228  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
13229  TrainDataVector.clear();
13230  Utilities->CallLogPop(1822);
13231  return(false);
13232  }
13233  }
13234  }
13235  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
13236  {
13237  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13238  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13239  if(AVEntry0.SignallerControl)
13240  {
13241  if(TrainDataVector.at(x).ActionVector.size() > 2)
13242  {
13243  SecondPassMessage(GiveMessages,
13244  "Error in timetable - a signaller controlled service can have no more than one item (a repeat) after the start event, see: " +
13245  TDEntry.HeadCode);
13246  TrainDataVector.clear();
13247  Utilities->CallLogPop(1837);
13248  return(false);
13249  }
13250  if(TrainDataVector.at(x).ActionVector.size() > 1)
13251  {
13252  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13253  if(AVEntry1.FormatType != Repeat)
13254  {
13255  SecondPassMessage(GiveMessages,
13256  "Error in timetable - a signaller controlled service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
13257  TrainDataVector.clear();
13258  Utilities->CallLogPop(1838);
13259  return(false);
13260  }
13261  }
13262  }
13263  }
13264  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
13265  {
13266  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13267  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13268  if(AVEntry0.SequenceType != StartSequence)
13269  {
13270  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
13271  TrainDataVector.clear();
13272  Utilities->CallLogPop(1824);
13273  return(false);
13274  }
13275  if((AVEntry0.Command == "Snt") && !AVEntry0.SignallerControl) // (4a) sig control condition added so there is a second AVEntry
13276  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
13277  // and others for a located Snt, but those checks done later
13278  {
13279  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1); // must be a second entry if first not signallercontrol
13280  if((AVEntry1.SequenceType == FinishSequence) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
13281  {
13282  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
13283  TDEntry.HeadCode); //these are the only AtLoc finishes not allowed
13284  TrainDataVector.clear();
13285  Utilities->CallLogPop(2046);
13286  return(false);
13287  }
13288  }
13289  if((AVEntry0.Command == "Sns") || (AVEntry0.Command == "Sfs")) // (4b)
13290  // 4b added at v2.15.0
13291  {
13292  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13293  if((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo"))
13294  {
13295  SecondPassMessage(GiveMessages, "Error in timetable - only 'Frh' or 'Fjo' finish events are permitted immediately after "
13296  "an 'Sns' or 'Sfs' event for: " + TDEntry.HeadCode + ". The program is unable to determine the "
13297  "location of any other type of finish.");
13298  TrainDataVector.clear();
13299  Utilities->CallLogPop(2580);
13300  return(false);
13301  }
13302  }
13303  if((AVEntry0.Command == "Snt-sh") || (AVEntry0.Command == "Sns-sh") || (AVEntry0.Command == "Sns-fsh")) // (4c)
13304  // 4c added at v2.15.0
13305  {
13306  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13307  if(AVEntry1.SequenceType == FinishSequence)
13308  {
13309  SecondPassMessage(GiveMessages, "Error in timetable - a finish event can't immediately follow an 'Snt-sh', 'Sns-sh' or 'Sns-fsh' "
13310  "event for: " + TDEntry.HeadCode);
13311  TrainDataVector.clear();
13312  Utilities->CallLogPop(2616);
13313  return(false);
13314  }
13315  }
13316  }
13317  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
13318  {
13319  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13320  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13321  {
13322  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13323  if((AVEntry.SequenceType == StartSequence) && (y != 0))
13324  {
13325  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
13326  TrainDataVector.clear();
13327  Utilities->CallLogPop(1825);
13328  return(false);
13329  }
13330  }
13331  }
13332  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
13333  {
13334  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13335  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13336  {
13337  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13338  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
13339  {
13340  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
13341  TrainDataVector.clear();
13342  Utilities->CallLogPop(1826);
13343  return(false);
13344  }
13345  }
13346  }
13347  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
13348  {
13349  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13350  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13351  {
13352  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13353  if((y == 0) && AVEntry.SignallerControl)
13354  {
13355  break;
13356  }
13357  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
13358  {
13359  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != FinishSequence))
13360  {
13361  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
13362  TrainDataVector.clear();
13363  Utilities->CallLogPop(1827);
13364  return(false);
13365  }
13366  if(AVEntry.FormatType == Repeat)
13367  {
13368  const TActionVectorEntry &LastButOneAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
13369  if(LastButOneAVEntry.SequenceType != FinishSequence)
13370  {
13371  SecondPassMessage(GiveMessages, "Error in timetable - the event immediately before the repeat must be a finish for: " + TDEntry.HeadCode);
13372  TrainDataVector.clear();
13373  Utilities->CallLogPop(1828);
13374  return(false);
13375  }
13376  }
13377  }
13378  }
13379  }
13380  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
13381  {
13382  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13383  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13384  {
13385  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13386  if(AVEntry.SequenceType == FinishSequence)
13387  {
13388  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
13389  {
13390  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
13391  TrainDataVector.clear();
13392  Utilities->CallLogPop(1829);
13393  return(false);
13394  }
13395  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
13396  {
13397  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13398  {
13399  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " + TDEntry.HeadCode);
13400  TrainDataVector.clear();
13401  Utilities->CallLogPop(1830);
13402  return(false);
13403  }
13404  }
13405  }
13406  }
13407  }
13408 
13409  // end of new preliminary checks
13410 
13411  // check start event successor validity
13412  // For Snt & Snt-sh set location if stopped, don't set any times yet
13413  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13414  {
13415  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13416  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13417  // use reference so can change internals where necessary
13418  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
13419  {
13420  AnsiString LocationName = "";
13421  if(IsSNTEntryLocated(0, TDEntry, LocationName))
13422  // it is at a location
13423  {
13424  AVEntry0.LocationName = LocationName; //located Snt location name set
13425  AVEntry0.LocationType = AtLocation;
13426  // check successor validity for located Snt that isn't a SignallerControl entry
13427  if(!AVEntry0.SignallerControl)
13428  {
13429  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13430  // at least 2 entries present checked in integrity check so (1) valid
13431  if(!AtLocSuccessor(AVEntry1))
13432  {
13433  // Frh following Snt-sh will return false in location check, so no need to check here
13434  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
13435  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13436  TrainDataVector.clear();
13437  Utilities->CallLogPop(523);
13438  return(false);
13439  }
13440  }
13441  }
13442  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
13443  {
13444  if(AVEntry0.Command == "Snt-sh")
13445  {
13446  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
13447  TrainDataVector.clear();
13448  Utilities->CallLogPop(1042);
13449  return(false);
13450  }
13451  AVEntry0.LocationType = EnRoute;
13452  if(!AVEntry0.SignallerControl)
13453  {
13454  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13455  // at least 2 entries checked in integrity check so (1) valid
13456  if(!MovingSuccessor(AVEntry1))
13457  {
13458  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
13459  TDEntry.HeadCode);
13460  TrainDataVector.clear();
13461  Utilities->CallLogPop(790);
13462  return(false);
13463  }
13464  }
13465  }
13466  }
13467  // check other start successors, all AtLoc
13468  else if(AVEntry0.SequenceType == StartSequence)
13469  {
13470  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
13471  // at least 2 entries present checked in integrity check so (1) valid
13472  if(!AtLocSuccessor(AVEntry1))
13473  {
13474  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh', 'Snt-fsh' or 'Sns-fsh' followed by an illegal event for: " +
13475  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
13476  TrainDataVector.clear();
13477  Utilities->CallLogPop(793);
13478  return(false);
13479  }
13480  }
13481  }
13482 
13483 
13484  // set Sns-sh & Sns-fsh locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
13485  for(unsigned int x = 0; x < TrainDataVector.size(); x++) //at v2.15.0 set Sfs & Sns locations from corresponding fsp/rsp & Fns entries, shuttles ok as they are
13486  {
13487  bool FoundFlag = false;
13488  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13489  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13490  // use reference so can change internals
13491  if((AVEntry0.Command == "Sns-sh") || (AVEntry0.Command == "Sns-fsh"))
13492  {
13493  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13494  {
13495  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
13496  if(AVEntry2.FormatType == TimeLoc)
13497  {
13498  FoundFlag = true;
13499  AVEntry0.LocationName = AVEntry2.LocationName; //Sns-sh & Sns-fsh location names set
13500  break;
13501  }
13502  }
13503  if(!FoundFlag)
13504  {
13505  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sns-sh' or 'Sns-fsh' event for: " + TDEntry.HeadCode);
13506  TrainDataVector.clear();
13507  Utilities->CallLogPop(851);
13508  return(false);
13509  }
13510  }
13511  }
13512 
13513 //carry out preliminary check on service ref linkages without setting any data - added at v2.15.0 as can be location errors if linked trains not present
13514 //first check for duplicates then linkages (also checked later but leave that in)
13515  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13516  {
13517  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13518  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13519  {
13520  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13521  if(AVEntry.OtherHeadCode != "")
13522  {
13523  if(!CheckForDuplicateCrossReferences(2, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13524  {
13525  Utilities->CallLogPop(2610);
13526  return(false); // error message given in called function
13527  }
13528  }
13529  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13530  {
13531  if(!CheckForDuplicateCrossReferences(3, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13532  {
13533  Utilities->CallLogPop(2611);
13534  return(false); // error message given in called function
13535  }
13536  }
13537  }
13538  }
13539 //cross reference check
13540  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13541  {
13542  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13543  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13544  {
13545  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13546  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13547  {
13548  if(AVEntry.OtherHeadCode != "")
13549  {
13550  if(!CheckCrossReferencesAndSetData(2, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, false, GiveMessages))
13551  // false = non-shuttle
13552  {
13553  Utilities->CallLogPop(2612);
13554  return(false); // error message given in called function
13555  }
13556  }
13557  }
13558  }
13559  }
13560 
13561 // now repeat the check just for the shuttles
13562  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13563  {
13564  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13565  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13566  {
13567  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13568  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13569  {
13570  if(AVEntry.OtherHeadCode != "")
13571  {
13572  if(!CheckCrossReferencesAndSetData(3, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, false, GiveMessages))
13573  // true = shuttle
13574  {
13575  Utilities->CallLogPop(2613);
13576  return(false); // error message given in called function
13577  }
13578  }
13579  }
13580  }
13581  }
13582 
13583 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13584  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13585  {
13586  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13587  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13588  {
13589  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13590  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13591  {
13592  if(!CheckNonRepeatingShuttleLinksAndSetData(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, false, GiveMessages))
13593  {
13594  Utilities->CallLogPop(2614);
13595  return(false); // error message given in called function
13596  }
13597  }
13598  }
13599  }
13600 
13601 //at v2.15.0 we want to set Sns, Sfs location names, but to set Sns & Sfs first need the linked Fns & fsp/rsp to have locations set as they aren't yet,
13602 //and before v2.15.0 they were set from the corresponding Sns & Sfs locations, which in turn were set from later TimeLoc departures. At v2.15.0 it
13603 //is required to have these commands followed by Frh & Fjo, so this is why we need the linked Fns & fsp/rsp to have locations set first. Now all Fns
13604 //will have a TimeLoc before, so that can provide its location, but fsp/rsp? Must they have a TimeLoc before? No, and can't rely on starting Sfs
13605 //having the location set yet. So, new restriction, insist on an rsp/fsp having a TimeLoc before it or a located Snt, and use one of those to set the
13606 //location for the rsp/fsp and hence the linked Sfs.
13607 
13608 //NB can't allow an Sfs to be followed by another split or won't find a name, test with many existing tts then add an error to find it
13609 //Fns must be preceded by an arrival
13610 
13611 //set name for Fns from earlier location name or fail if can't find
13612  bool LocFoundFlag, FnsFoundFlag;
13613  TActionVectorEntry *AVEntryFns;
13614  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13615  {
13616  LocFoundFlag = false;
13617  FnsFoundFlag = false;
13618  for(int y = TrainDataVector.at(x).ActionVector.size() - 1; y >= 0; y--) //search backwards
13619  {
13620  if(TrainDataVector.at(x).ActionVector.at(y).Command == "Fns")
13621  {
13622  AVEntryFns = &TrainDataVector.at(x).ActionVector.at(y);
13623  FnsFoundFlag = true;
13624  continue;
13625  }
13626  if(!FnsFoundFlag)
13627  {
13628  continue;
13629  }
13630  if(TrainDataVector.at(x).ActionVector.at(y).LocationName != "")
13631  {
13632  LocFoundFlag = true;
13633  AVEntryFns->LocationName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13634 // double EVT = double(AVEntryFns->EventTime); //test
13635  break; //name found
13636  }
13637  if(TrainDataVector.at(x).ActionVector.at(y).LocationType == AtLocation) //not named yet
13638  {
13639  continue;
13640  }
13641  else
13642  {
13643  SecondPassMessage(GiveMessages, "Error in timetable - the program can't determine the location of an 'Fns' finish, it must be preceded "
13644  "by an event at the same location that has an identified location name, normally an arrival, see "
13645  + TrainDataVector.at(x).ServiceReference);
13646  TrainDataVector.clear();
13647  Utilities->CallLogPop(2596);
13648  return(false);
13649  }
13650  }
13651  if(FnsFoundFlag && !LocFoundFlag)
13652  {
13653  SecondPassMessage(GiveMessages, "Error in timetable - the program can't determine the location of an 'Fns' finish, it must be preceded "
13654  "by an event at the same location that has an identified location name, normally an arrival, see "
13655  + TrainDataVector.at(x).ServiceReference);
13656  TrainDataVector.clear();
13657  Utilities->CallLogPop(2597);
13658  return(false);
13659  }
13660  }
13661 
13662 //now set all names for Sns entries from the above, new at v2.15.0
13663  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13664  {
13665  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13666  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13667  // use reference so can change internals
13668  if(AVEntry0.Command == "Sns")
13669  {
13670  //new at v2.15.0. Only set location if have a forward and backward linkage at same time (these all set when SecondPassActions called).
13671  //This isn't rigorous as may have more than one, but if do then will be caught below in CheckCrossReferencesAndSetData
13672  //If fail to find location just ignore as will be caught later in CheckCrossReferencesAndSetData
13673  //note that at this stage the OtherHeadCode values are service refs, as haven't yet been changed back to headcodes until
13674  //StripExcessFromHeadCode called at end of this function
13675  //need to be the same: forward & reverse service refs, event times, commands correspond
13676 
13677  //successor checks first: /no starts, /finishes except Frh & Fjo (as of v2.15.0), /repeats, /pas, /TimeTimeLoc or /TimeLoc arr; any other cmd or TimeLoc (dep) OK
13678  if(TDEntry.ActionVector.size() < 2)
13679  {
13680  SecondPassMessage(GiveMessages, "Error in timetable - insufficient actions follwing an 'Sns' event for: " + TDEntry.HeadCode);
13681  TrainDataVector.clear();
13682  Utilities->CallLogPop(2598);
13683  return(false);
13684  }
13685  TActionVectorEntry AVEntry1 = TDEntry.ActionVector.at(1);
13686  if(!AtLocSuccessor(AVEntry1))
13687  {
13688  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sns' event is followed by an illegal event for: " + TDEntry.HeadCode +
13689  ". The event isn't valid for a stationary train.");
13690  TrainDataVector.clear();
13691  Utilities->CallLogPop(2599);
13692  return(false);
13693  }
13694  if((AVEntry1.SequenceType == StartSequence) || ((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo")) ||
13695  (AVEntry1.FormatType == Repeat))
13696  {
13697  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sns' event is followed by an illegal event for: " + TDEntry.HeadCode);
13698  TrainDataVector.clear();
13699  Utilities->CallLogPop(2600);
13700  return(false);
13701  }
13702 
13703  //now set the location and location type
13704  TDateTime SnsEventTime = AVEntry0.EventTime;
13705 // double EVT = double(SnsEventTime); //test
13706  AnsiString SnsServiceRef = TDEntry.ServiceReference;
13707  bool BreakFlag = false;
13708  for(unsigned int y = 0; y < TrainDataVector.size(); y++)
13709  {
13710  for(unsigned int z = 0; z < TrainDataVector.at(y).ActionVector.size(); z++)
13711  {
13712  if((TrainDataVector.at(y).ActionVector.at(z).Command == "Fns") && (SnsEventTime == TrainDataVector.at(y).ActionVector.at(z).EventTime) &&
13713  (TrainDataVector.at(y).HeadCode == AVEntry0.OtherHeadCode))
13714  { //forward linkage found
13715  if(TrainDataVector.at(y).ActionVector.at(z).OtherHeadCode == SnsServiceRef) //OtherHeadCode values are service refs, see above
13716  { //reverse linkage found
13717  AVEntry0.LocationName = TrainDataVector.at(y).ActionVector.at(z).LocationName;
13718  AVEntry0.LocationType = AtLocation;
13719  BreakFlag = true;
13720  break;
13721  }
13722  }
13723  }
13724  if(BreakFlag)
13725  {
13726  break;
13727  }
13728  }
13729  //test for any unnamed AtLoc entries at end of name setting
13730  }
13731  }
13732 
13733 //trap errors where rsp/fsp follows an Sfs without a TimeLoc arrival before (or unlikely to be able to set fsp/rsp/Sfs location because Sfs locs set from linked fsp/rsp events)
13734  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13735  {
13736  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13737  if(AVEntry0.Command == "Sfs")
13738  {
13739  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
13740  {
13741  if(TrainDataVector.at(x).ActionVector.at(y).LocationName != "") //must be a timeloc as only they have loc set and are AtLoc (non-AtLoc trapped above)
13742  {
13743  break;
13744  }
13745  else if((TrainDataVector.at(x).ActionVector.at(y).Command == "fsp") || (TrainDataVector.at(x).ActionVector.at(y).Command == "rsp"))
13746  {
13747  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event must be followed by a departure and arrival before another split, see " + TrainDataVector.at(x).ServiceReference);
13748  TrainDataVector.clear();
13749  Utilities->CallLogPop(2586);
13750  return(false);
13751  }
13752  }
13753  }
13754  }
13755 
13756 //now name fsp/rsp actions
13757  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13758  {
13759  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13760  {
13761  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13762  if(AVEntry.LocationName != "")
13763  {
13764  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13765  {
13766  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13767  // use reference so can change internals where necessary
13768  if((AVEntry2.Command == "fsp") || (AVEntry2.Command == "rsp"))
13769  {
13770  AVEntry2.LocationName = AVEntry.LocationName;
13771  } //test for any unnamed AtLoc entries at end of name setting
13772  else if(AVEntry2.LocationType != AtLocation)
13773  {
13774  break;
13775  }
13776  }
13777  }
13778  }
13779  }
13780 
13781 //check that all named or give error message
13782  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13783  {
13784  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13785  {
13786  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13787  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
13788  {
13789  if(AVEntry.LocationName == "")
13790  {
13791  SecondPassMessage(GiveMessages, "Error in timetable - an 'fsp' or 'rsp' event must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13792  TrainDataVector.clear();
13793  Utilities->CallLogPop(2617);
13794  return(false);
13795  }
13796  }
13797  }
13798  }
13799 
13800 //now set all Sfs entries from the above
13801  for(unsigned int x = 0; x < TrainDataVector.size(); x++) //at v2.15.0 set Sfs & Sns locations from corresponding fsp/rsp & Fns entries, shuttles ok as they are
13802  {
13803  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13804  TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
13805  // use reference so can change internals
13806  if(AVEntry0.Command == "Sfs")
13807  {
13808  //new at v2.15.0. Only set location if have a forward and backward linkage at same time (these all set when SecondPassActions called). This isn't
13809  //rigorous as may have more than one, but if do then will be caught below in CheckCrossReferencesAndSetData
13810  //If fail to find location just ignore as will be caught later in CheckCrossReferencesAndSetData
13811  //note that at this stage the OtherHeadCode values are service refs, as haven't yet been changed back to headcodes until StripExcessFromHeadCode called
13812  //at end of this function
13813  //need to be the same: forward & reverse service refs, event times, commands correspond
13814 
13815  //successor checks first: /no starts, /finishes except Frh & Fjo (as of v2.15.0), /repeats, /pas, /TimeTimeLoc or /TimeLoc arr; any other cmd or TimeLoc (dep) OK
13816  if(TDEntry.ActionVector.size() < 2)
13817  {
13818  SecondPassMessage(GiveMessages, "Error in timetable - insufficient actions follwing an 'Sfs' event for: " + TDEntry.HeadCode);
13819  TrainDataVector.clear();
13820  Utilities->CallLogPop(2587);
13821  return(false);
13822  }
13823  TActionVectorEntry AVEntry1 = TDEntry.ActionVector.at(1);
13824  if(!AtLocSuccessor(AVEntry1))
13825  {
13826  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event is followed by an illegal event for: " + TDEntry.HeadCode +
13827  ". The event isn't valid for a stationary train.");
13828  TrainDataVector.clear();
13829  Utilities->CallLogPop(2588);
13830  return(false);
13831  }
13832  if((AVEntry1.SequenceType == StartSequence) || ((AVEntry1.SequenceType == FinishSequence) && (AVEntry1.Command != "Frh") && (AVEntry1.Command != "Fjo")) ||
13833  (AVEntry1.FormatType == Repeat))
13834  {
13835  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' event is followed by an illegal event for: " + TDEntry.HeadCode);
13836  TrainDataVector.clear();
13837  Utilities->CallLogPop(2589);
13838  return(false);
13839  }
13840 
13841  //now set the location and location type
13842  TDateTime SfsEventTime = AVEntry0.EventTime;
13843  AnsiString SfsServiceRef = TDEntry.ServiceReference;
13844  bool BreakFlag = false;
13845  for(unsigned int y = 0; y < TrainDataVector.size(); y++)
13846  {
13847  for(unsigned int z = 0; z < TrainDataVector.at(y).ActionVector.size(); z++)
13848  {
13849  if(((TrainDataVector.at(y).ActionVector.at(z).Command == "fsp") || (TrainDataVector.at(y).ActionVector.at(z).Command == "rsp")) &&
13850  (SfsEventTime == TrainDataVector.at(y).ActionVector.at(z).EventTime) && (TrainDataVector.at(y).HeadCode == AVEntry0.OtherHeadCode))
13851  { //forward linkage found
13852  if(TrainDataVector.at(y).ActionVector.at(z).OtherHeadCode == SfsServiceRef) //OtherHeadCode values are service refs, see above
13853  { //reverse linkage found
13854  AVEntry0.LocationName = TrainDataVector.at(y).ActionVector.at(z).LocationName;
13855  AVEntry0.LocationType = AtLocation;
13856  BreakFlag = true;
13857  break;
13858  }
13859  }
13860  }
13861  if(BreakFlag)
13862  {
13863  break;
13864  }
13865  } //test for any unnamed AtLoc entries at end of name setting
13866  }
13867  }
13868 
13869  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
13870  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13871  {
13872  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13873  {
13874  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13875  if(AVEntry.LocationName != "")
13876  {
13877  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
13878  {
13879  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
13880  // use reference so can change internals where necessary
13881  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
13882  {
13883  AVEntry2.LocationName = AVEntry.LocationName;
13884  } //test for any unnamed AtLoc entries at end of name setting
13885  else
13886  {
13887  break;
13888  }
13889  }
13890  }
13891  }
13892  }
13893  // all location names should now be set
13894 
13895 //now test for any unnamed AtLoc entries where Command != "" and give message if find any - shouldn't find any if above checks comprehensive
13896  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13897  {
13898  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13899  {
13900  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13901  if((AVEntry.LocationType == AtLocation) && (AVEntry.LocationName == "") && (AVEntry.Command != ""))
13902  {
13903  if((AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "cdt") || (AVEntry.Command == "dsc"))
13904  {
13905  SecondPassMessage(GiveMessages, "Error in timetable - '" + AVEntry.Command + "' must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13906  TrainDataVector.clear();
13907  Utilities->CallLogPop(2619);
13908  return(false);
13909  }
13910  else if((AVEntry.Command == "Sns") || (AVEntry.Command == "Sfs"))
13911  {
13912  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
13913  "Please make sure that the finish event of the service that links to this event is preceded by an "
13914  "event at the same location that has an identified location name, normally an arrival.");
13915  TrainDataVector.clear();
13916  Utilities->CallLogPop(2620);
13917  return(false);
13918  }
13919  else if(AVEntry.Command == "Snt-sh")
13920  {
13921  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
13922  "Please make sure that the service starts with zero speed and is at a named location.");
13923  TrainDataVector.clear();
13924  Utilities->CallLogPop(2623);
13925  return(false);
13926  }
13927  else if((AVEntry.Command == "Sns-fsh") || (AVEntry.Command == "Sns-sh"))
13928  {
13929  SecondPassMessage(GiveMessages, "Error in timetable - the location of the '" + AVEntry.Command + "' event in service " + TrainDataVector.at(x).ServiceReference + " can't be identified. " +
13930  "Please make sure that the event is followed (not necessarily immediately) by a departure.");
13931  TrainDataVector.clear();
13932  Utilities->CallLogPop(2622);
13933  return(false);
13934  }
13935  else if((AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"))
13936  {
13937  SecondPassMessage(GiveMessages, "Error in timetable - '" + AVEntry.Command + "must be preceded by an event at the same location that has an identified location name, normally an arrival, see " + TrainDataVector.at(x).ServiceReference);
13938  TrainDataVector.clear();
13939  Utilities->CallLogPop(2621);
13940  return(false);
13941  }
13942  }
13943  }
13944  }
13945 
13946  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
13947  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13948  {
13949  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13950  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13951  {
13952  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13953  if((AVEntry.SequenceType == FinishSequence) && (AVEntry.Command != "F-nshs"))
13954  {
13955  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
13956  // i.e at least one more, must be a repeat
13957  {
13958  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
13959  {
13960  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish event for: " + TDEntry.HeadCode);
13961  TrainDataVector.clear();
13962  Utilities->CallLogPop(798);
13963  return(false);
13964  }
13965  }
13966  }
13967  if(AVEntry.Command == "F-nshs")
13968  {
13969  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
13970  // i.e has to be the last
13971  {
13972  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
13973  TrainDataVector.clear();
13974  Utilities->CallLogPop(1049);
13975  return(false);
13976  }
13977  }
13978  if(AVEntry.Command == "pas")
13979  {
13980  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13981  {
13982  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
13983  TrainDataVector.clear();
13984  Utilities->CallLogPop(1518);
13985  return(false);
13986  }
13987  }
13988  if(AVEntry.Command == "jbo")
13989  {
13990  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
13991  {
13992  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
13993  TrainDataVector.clear();
13994  Utilities->CallLogPop(800);
13995  return(false);
13996  }
13997  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
13998  if(!AtLocSuccessor(AVEntry2))
13999  {
14000  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
14001  ". The event isn't valid for a stationary train.");
14002  TrainDataVector.clear();
14003  Utilities->CallLogPop(801);
14004  return(false);
14005  }
14006  }
14007  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
14008  {
14009  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14010  {
14011  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
14012  TrainDataVector.clear();
14013  Utilities->CallLogPop(802);
14014  return(false);
14015  }
14016  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14017  if(!AtLocSuccessor(AVEntry2))
14018  {
14019  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
14020  ". The event isn't valid for a stationary train.");
14021  TrainDataVector.clear();
14022  Utilities->CallLogPop(803);
14023  return(false);
14024  }
14025  }
14026  if(AVEntry.Command == "cdt")
14027  {
14028  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14029  {
14030  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
14031  TrainDataVector.clear();
14032  Utilities->CallLogPop(804);
14033  return(false);
14034  }
14035  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14036  if(!AtLocSuccessor(AVEntry2))
14037  {
14038  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
14039  ". The event isn't valid for a stationary train.");
14040  TrainDataVector.clear();
14041  Utilities->CallLogPop(805);
14042  return(false);
14043  }
14044  }
14045  if(AVEntry.Command == "dsc")
14046  {
14047  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14048  {
14049  SecondPassMessage(GiveMessages, "Error in timetable - a 'dsc' can't be the last event for: " + TDEntry.HeadCode);
14050  TrainDataVector.clear();
14051  Utilities->CallLogPop(2602);
14052  return(false);
14053  }
14054  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14055  if(!AtLocSuccessor(AVEntry2))
14056  {
14057  SecondPassMessage(GiveMessages, "Error in timetable - a 'dsc' is followed by an illegal event for: " + TDEntry.HeadCode +
14058  ". The event isn't valid for a stationary train.");
14059  TrainDataVector.clear();
14060  Utilities->CallLogPop(2603);
14061  return(false);
14062  }
14063  }
14064  if(AVEntry.FormatType == TimeTimeLoc)
14065  {
14066  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14067  {
14068  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
14069  TrainDataVector.clear();
14070  Utilities->CallLogPop(806);
14071  return(false);
14072  }
14073  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14074  if(!MovingSuccessor(AVEntry2))
14075  {
14076  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
14077  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
14078  TrainDataVector.clear();
14079  Utilities->CallLogPop(807);
14080  return(false);
14081  }
14082  }
14083  if(AVEntry.FormatType == PassTime)
14084  {
14085  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14086  {
14087  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
14088  TrainDataVector.clear();
14089  Utilities->CallLogPop(1530);
14090  return(false);
14091  }
14092  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14093  if(!MovingSuccessor(AVEntry2))
14094  {
14095  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
14096  ". The event isn't valid for a moving train.");
14097  TrainDataVector.clear();
14098  Utilities->CallLogPop(1531);
14099  return(false);
14100  }
14101  }
14102  if(AVEntry.FormatType == Repeat)
14103  {
14104  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
14105  {
14106  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
14107  TrainDataVector.clear();
14108  Utilities->CallLogPop(808);
14109  return(false);
14110  }
14111  }
14112  }
14113  }
14114 
14115  // set arrival & departure times for TimeLocs & set their EventTimes to -1
14116  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14117  {
14118  bool LastEntryIsAnArrival = false;
14119  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14120  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
14121  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
14122  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
14123  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
14124  {
14125  LastEntryIsAnArrival = false;
14126  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14127  {
14128  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14129  if(AVEntry.FormatType == TimeLoc)
14130  {
14131  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
14132  {
14133  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
14134  }
14135  if(LastEntryIsAnArrival)
14136  {
14137  AVEntry.DepartureTime = AVEntry.EventTime;
14138  AVEntry.EventTime = TDateTime(-1);
14139  LastEntryIsAnArrival = false;
14140  }
14141  else // last entry a departure
14142  {
14143  AVEntry.ArrivalTime = AVEntry.EventTime;
14144  AVEntry.EventTime = TDateTime(-1);
14145  LastEntryIsAnArrival = true;
14146  }
14147  }
14148  }
14149  }
14150  else // all others stopped at beginning
14151  {
14152  LastEntryIsAnArrival = true;
14153  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14154  {
14155  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14156  if(AVEntry.FormatType == TimeLoc)
14157  {
14158  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
14159  {
14160  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
14161  }
14162  if(LastEntryIsAnArrival)
14163  {
14164  AVEntry.DepartureTime = AVEntry.EventTime;
14165  AVEntry.EventTime = TDateTime(-1);
14166  LastEntryIsAnArrival = false;
14167  }
14168  else // last entry a departure
14169  {
14170  AVEntry.ArrivalTime = AVEntry.EventTime;
14171  AVEntry.EventTime = TDateTime(-1);
14172  LastEntryIsAnArrival = true;
14173  }
14174  }
14175  }
14176  }
14177  }
14178  // perform remaining successor checks for TimeLocs
14179  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14180  {
14181  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14182  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14183  {
14184  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14185  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
14186  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
14187  {
14188  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14189  {
14190  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
14191  TrainDataVector.clear();
14192  Utilities->CallLogPop(809);
14193  return(false);
14194  }
14195  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14196  if(!AtLocSuccessor(AVEntry2))
14197  {
14198  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
14199  ". The event isn't valid for a stationary train.");
14200  TrainDataVector.clear();
14201  Utilities->CallLogPop(810);
14202  return(false);
14203  }
14204  }
14205  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
14206  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
14207  {
14208  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
14209  {
14210  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
14211  TrainDataVector.clear();
14212  Utilities->CallLogPop(811);
14213  return(false);
14214  }
14215  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
14216  if(!MovingSuccessor(AVEntry2))
14217  {
14218  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
14219  ". The event isn't valid for a moving train.");
14220  TrainDataVector.clear();
14221  Utilities->CallLogPop(812);
14222  return(false);
14223  }
14224  }
14225  }
14226  }
14227 
14228  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
14229  // & repeats have no times set
14230  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14231  {
14232  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14233  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14234  {
14235  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14236  if(AVEntry.FormatType == TimeLoc)
14237  {
14238  if(AVEntry.EventTime != TDateTime(-1))
14239  {
14240  throw Exception("Timetable error, TimeLoc event has EventTime not -1 for " + TDEntry.HeadCode);
14241  }
14242  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
14243  {
14244  throw Exception("Timetable error, TimeLoc event has neither arrival nor departure time set for " + TDEntry.HeadCode);
14245  }
14246  }
14247  if(AVEntry.FormatType == TimeTimeLoc)
14248  {
14249  if(AVEntry.EventTime != TDateTime(-1))
14250  {
14251  throw Exception("Timetable error, TimeTimeLoc event has EventTime not -1 for " + TDEntry.HeadCode);
14252  }
14253  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
14254  {
14255  throw Exception("Timetable error, TimeTimeLoc event has either arrival or departure time not set for " + TDEntry.HeadCode);
14256  }
14257  }
14258  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
14259  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
14260  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
14261  {
14262  if(AVEntry.EventTime == TDateTime(-1))
14263  {
14264  throw Exception("Timetable error, Cmd or PassTime event has EventTime not set for " + TDEntry.HeadCode);
14265  }
14266  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
14267  {
14268  throw Exception("Timetable error, Cmd or PassTime event has either arrival or departure time set for " + TDEntry.HeadCode);
14269  }
14270  }
14271  if(AVEntry.FormatType == Repeat)
14272  {
14273  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
14274  {
14275  throw Exception("Timetable error, Repeat event has a time set for " + TDEntry.HeadCode);
14276  }
14277  }
14278  }
14279  }
14280 
14281  // check times stay same or increase, note that can have time of 0 if include midnight
14282  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14283  {
14284  TDateTime CurrentTime = TTClockTime; // the timetable start time
14285  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14286  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14287  {
14288  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14289  if(AVEntry.FormatType == Repeat)
14290  {
14291  break;
14292  }
14293  if(AVEntry.FormatType == FinRemHere)
14294  {
14295  break;
14296  }
14297  if(AVEntry.FormatType == TimeTimeLoc)
14298  {
14299  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
14300  {
14301  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
14302  TDEntry.HeadCode);
14303  TrainDataVector.clear();
14304  Utilities->CallLogPop(813);
14305  return(false);
14306  }
14307  if(AVEntry.ArrivalTime < CurrentTime)
14308  {
14309  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
14310  TDEntry.HeadCode);
14311  TrainDataVector.clear();
14312  Utilities->CallLogPop(814);
14313  return(false);
14314  }
14315  CurrentTime = AVEntry.DepartureTime;
14316  continue;
14317  }
14318  if(AVEntry.FormatType == TimeLoc)
14319  {
14320  if(AVEntry.ArrivalTime >= TDateTime(0))
14321  {
14322  if(AVEntry.ArrivalTime < CurrentTime)
14323  {
14324  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
14325  TrainDataVector.clear();
14326  Utilities->CallLogPop(815);
14327  return(false);
14328  }
14329  CurrentTime = AVEntry.ArrivalTime;
14330  }
14331  else
14332  {
14333  if(AVEntry.DepartureTime < CurrentTime)
14334  // both may be 0 legitimately so must allow for this
14335  {
14336  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
14337  TrainDataVector.clear();
14338  Utilities->CallLogPop(816);
14339  return(false);
14340  }
14341  CurrentTime = AVEntry.DepartureTime;
14342  }
14343  continue;
14344  }
14345  if(AVEntry.EventTime < CurrentTime)
14346  // all others have EventTime set
14347  {
14348  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
14349  ", may be before timetable start time");
14350  TrainDataVector.clear();
14351  Utilities->CallLogPop(835);
14352  return(false);
14353  }
14354  CurrentTime = AVEntry.EventTime;
14355  continue;
14356  }
14357  }
14358 
14359  // check locations consistent
14360  AnsiString LastLocationName = "";
14361 
14362  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14363  {
14364  bool LastEntryIsAnArrival = false;
14365  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14366  // first deal with moving Snt entries (all else stopped)
14367  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
14368  {
14369  LastEntryIsAnArrival = false;
14370  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
14371  if(LastLocationName != "")
14372  {
14373  throw Exception("Timetable error, moving Snt event has LocationName set for " + TDEntry.HeadCode);
14374  }
14375  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
14376  y++) // note that immediate successor to a moving Snt can only be a Moving type
14377  {
14378  // if it's a SignallerControl entry then the condition isn't met
14379  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14380  if(AVEntry.FormatType == Repeat)
14381  {
14382  break; // repeat = reached end (+allows repeat after signaller controlled entry)
14383  }
14384  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
14385  {
14386  if(AVEntry.LocationName != LastLocationName)
14387  {
14388  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14389  AVEntry.Command);
14390  TrainDataVector.clear();
14391  Utilities->CallLogPop(823);
14392  return(false);
14393  }
14394  }
14395  else if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdDescription))
14396  // cdt is the only TimeCmd & dsc is the only TimeCmdDescription
14397  {
14398  if(AVEntry.LocationName != LastLocationName)
14399  {
14400  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14401  AVEntry.Command);
14402  TrainDataVector.clear();
14403  Utilities->CallLogPop(824);
14404  return(false);
14405  }
14406  }
14407  else if(AVEntry.FormatType == TimeTimeLoc)
14408  {
14409  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14410  // last entry must be a departure or would have failed earlier
14411  {
14412  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14413  TwoLocationFlag = true;
14414 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14415 // TwoOrMoreLocationsWarningGiven = true;
14416  }
14417  LastLocationName = AVEntry.LocationName;
14418  LastEntryIsAnArrival = false;
14419  }
14420  else if(AVEntry.FormatType == TimeLoc)
14421  {
14422  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
14423  {
14424  SecondPassMessage(GiveMessages,
14425  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
14426  TrainDataVector.clear();
14427  Utilities->CallLogPop(826);
14428  return(false);
14429  }
14430  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
14431  {
14432  SecondPassMessage(GiveMessages,
14433  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
14434  TrainDataVector.clear();
14435  Utilities->CallLogPop(827);
14436  return(false);
14437  }
14438  LastLocationName = AVEntry.LocationName;
14439  LastEntryIsAnArrival = !LastEntryIsAnArrival;
14440  }
14441  }
14442  }
14443  else // all stationary starting entries
14444  {
14445  LastEntryIsAnArrival = true;
14446  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
14447  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14448  {
14449  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14450  if(AVEntry.FormatType == Repeat)
14451  {
14452  break;
14453  }
14454  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
14455  // no need to add anything for shuttle starts since they are at loc (0) anyway
14456  {
14457  if(AVEntry.LocationName != LastLocationName)
14458  {
14459  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14460  AVEntry.Command);
14461  TrainDataVector.clear();
14462  Utilities->CallLogPop(828);
14463  return(false);
14464  }
14465  }
14466  else if(AVEntry.FormatType == TimeCmd)
14467  // cdt is the only TimeCmd
14468  {
14469  if(AVEntry.LocationName != LastLocationName)
14470  {
14471  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
14472  AVEntry.Command);
14473  TrainDataVector.clear();
14474  Utilities->CallLogPop(829);
14475  return(false);
14476  }
14477  }
14478  else if(AVEntry.FormatType == TimeTimeLoc)
14479  {
14480  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14481  // last entry must be a departure or would have failed earlier
14482  {
14483  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14484  TwoLocationFlag = true;
14485 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14486 // TwoOrMoreLocationsWarningGiven = true;
14487  }
14488  LastLocationName = AVEntry.LocationName;
14489  LastEntryIsAnArrival = false;
14490  }
14491  else if(AVEntry.FormatType == TimeLoc)
14492  {
14493  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
14494  {
14495  SecondPassMessage(GiveMessages,
14496  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
14497  TrainDataVector.clear();
14498  Utilities->CallLogPop(831);
14499  return(false);
14500  }
14501  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
14502  {
14503  SecondPassMessage(GiveMessages,
14504  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
14506 // TrainDataVector.clear();
14507 // Utilities->CallLogPop(832);
14508 // return false;
14509  }
14510  LastLocationName = AVEntry.LocationName;
14511  LastEntryIsAnArrival = !LastEntryIsAnArrival;
14512  }
14513  }
14514  }
14515  }
14516 
14517  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
14518  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
14519  AnsiString LocationNameToBeChecked = "";
14520 
14521  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14522  {
14523  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14524  unsigned int y = 0;
14525  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
14526  // first discard unlocated Snt entries as they don't have location name set
14527  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
14528  {
14529  y = 1;
14530  }
14531  while(y < TDEntry.ActionVector.size())
14532  // need to check each location name separately in turn, skipped for SignallerControl entries
14533  {
14534  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
14535  {
14536  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
14537  }
14538  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
14539  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
14540  {
14541  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
14542  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
14543  {
14544  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
14545  }
14546  if(AVEntry.Command == "cdt")
14547  {
14548  break; // out of the 'z' loop since the check is only valid up to a change of direction
14549  }
14550  if(AVEntry.LocationName == LocationNameToBeChecked)
14551  {
14552  continue; // keep going while name same
14553  }
14554  if(AVEntry.LocationName != LocationNameToBeChecked)
14555  // if name different check forwards to see if repeats
14556  {
14557  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
14558  {
14559  if(TDEntry.ActionVector.at(a).Command == "cdt")
14560  {
14561  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
14562  }
14563  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
14564  {
14565  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
14566  TwoLocationFlag = true;
14567 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
14568 // TwoOrMoreLocationsWarningGiven = true;
14569  }
14570  }
14571  break; // out of the 'z' loop since have checked 'a' as far as need to
14572  }
14573  }
14574  y++;
14575  }
14576  }
14577  if(TwoLocationFlag) //messages for this moved to InterfaceUnit - separate box listing potential errors and asking user to check
14578  {
14579  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
14580  TwoLocationList.unique(); //remove duplicates
14581  }
14582 
14583  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
14584  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14585  {
14586  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14587  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14588  {
14589  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14590  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
14591  {
14592  throw Exception("Error, non- 'Snt', 'Fer' or Repeat event doesn't have a location name set for " + TDEntry.HeadCode);
14593  }
14594  AnsiString LocName = "";
14595  // dummy, only used so can call IsSNTEntryLocated
14596  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
14597  {
14598  if(AVEntry.LocationName == "")
14599  {
14600  throw Exception("Error, 'Snt' event at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
14601  }
14602  }
14603  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
14604  {
14605  if(AVEntry.LocationName != "")
14606  {
14607  throw Exception("Error, 'Snt' unlocated event has a location name set for " + TDEntry.HeadCode);
14608  }
14609  }
14610  }
14611  }
14612 
14613 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
14614  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
14615  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
14616 
14617  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
14618  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
14619 
14620  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
14621  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
14622  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
14623 */
14624  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
14625  {
14626  // non-shuttles & non-repeating links separately, but don't check that there isn't a
14627  // duplicate between a non-repeating shuttle and another - leave original tests in as
14628  // these also set the pointers
14629  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14630  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14631  {
14632  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14633  if(AVEntry.OtherHeadCode != "")
14634  {
14635  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
14636  {
14637  Utilities->CallLogPop(1584);
14638  return(false); // error message given in called function
14639  }
14640  }
14641  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14642  {
14643  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
14644  {
14645  Utilities->CallLogPop(1585);
14646  return(false); // error message given in called function
14647  }
14648  }
14649  }
14650  }
14651 
14652  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14653  {
14654  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14655  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14656  {
14657  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14658  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
14659  {
14660  if(AVEntry.OtherHeadCode != "")
14661  {
14662  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, true, GiveMessages))
14663  // false = non-shuttle
14664  {
14665  Utilities->CallLogPop(864);
14666  return(false); // error message given in called function
14667  }
14668  }
14669  }
14670  }
14671  }
14672 
14673  // now repeat the check just for the shuttles
14674  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14675  {
14676  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14677  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14678  {
14679  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14680  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
14681  {
14682  if(AVEntry.OtherHeadCode != "")
14683  {
14684  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, true, GiveMessages))
14685  // true = shuttle
14686  {
14687  Utilities->CallLogPop(1100);
14688  return(false); // error message given in called function
14689  }
14690  }
14691  }
14692  }
14693  }
14694 
14695  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14696  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14697  {
14698  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14699  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14700  {
14701  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14702  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
14703  {
14704  if(!CheckNonRepeatingShuttleLinksAndSetData(0, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, true, GiveMessages))
14705  {
14706  Utilities->CallLogPop(1060);
14707  return(false); // error message given in called function
14708  }
14709  }
14710  }
14711  }
14712 
14713  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14714  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14715  // don't ever need to and as designed would skip repeats
14716  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14717  {
14718  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14719  {
14720  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14721  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
14722  {
14723  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
14724  {
14725  Utilities->CallLogPop(1090);
14726  return(false); // error message given in called function
14727  }
14728  }
14729  }
14730  }
14731 
14732  // check all entries have all types set to something
14733  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14734  {
14735  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14736  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14737  {
14738  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14739  if(AVEntry.FormatType == NoFormat)
14740  {
14741  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
14742  }
14743  else if(AVEntry.SequenceType == NoSequence)
14744  {
14745  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
14746  }
14747  else if(AVEntry.LocationType == NoLocation)
14748  {
14749  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
14750  }
14751  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
14752  {
14753  throw Exception("Error - timetable ActionVector event no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
14754  }
14755  }
14756  }
14757 
14758  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
14759  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14760  {
14761  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
14762  // non-const reference so can alter content
14763  TTrainOperatingData TData;
14764  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
14765  if(LastAVEntry.FormatType == Repeat) // check if a repeat
14766  {
14767 /*
14768  class TTrainOperatingData
14769  {
14770  public:
14771  int TrainID; - default, set at construction
14772  TActionEventType EventReported; used during operation
14773  TRunningEntry RunningEntry; - default, set at construction
14774  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
14775  };
14776 */
14777  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
14778  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
14779  {
14780  TDEntry.TrainOperatingDataVector.push_back(TData);
14781  }
14782  }
14783  else
14784  {
14785  TDEntry.NumberOfTrains = 1;
14786  }
14787  }
14788 
14789  // check that don't include any Continuation names
14790  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14791  {
14792  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14793  {
14794  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
14795  AnsiString HC = TrainDataVector.at(x).HeadCode;
14796  if(LocName != "")
14797  {
14798  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
14799  {
14800  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
14801  TrainDataVector.clear();
14802  Utilities->CallLogPop(1578);
14803  return(false);
14804  }
14805  }
14806  }
14807  }
14808 
14809  // check that all repeat times below 96h
14810  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14811  {
14812  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
14813  int IncMinutes = 0;
14814  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
14815  {
14816  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
14817  }
14818  else
14819  {
14820  continue; // basic times already checked in CheckTimeValidity
14821  }
14822  AnsiString HC = TrainDataVector.at(x).HeadCode;
14823  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14824  {
14825  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
14826  {
14827  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14828  {
14829  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
14830  TrainDataVector.clear();
14831  Utilities->CallLogPop(1818);
14832  return(false);
14833  }
14834  }
14835  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
14836  {
14837  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14838  // 3d 23h 59m = 3.9993055556
14839  {
14840  SecondPassMessage(GiveMessages, "Error in timetable - a repeat event time exceeds 95h 59m, see service " + HC);
14841  TrainDataVector.clear();
14842  Utilities->CallLogPop(1819);
14843  return(false);
14844  }
14845  }
14846  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
14847  {
14848  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
14849  // 3d 23h 59m = 3.9993055556
14850  {
14851  SecondPassMessage(GiveMessages, "Error in timetable - a repeat event time exceeds 95h 59m, see service " + HC);
14852  TrainDataVector.clear();
14853  Utilities->CallLogPop(1820);
14854  return(false);
14855  }
14856  }
14857  }
14858  }
14859 
14860  // Now that all set up change any extended headcodes back to ordinary headcodes
14861  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14862  {
14863  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
14864  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14865  {
14866  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
14867  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
14868  }
14869  }
14870 
14871  // SaveTrainDataVectorToFile(0);//for testing purposes
14873  Utilities->CallLogPop(782);
14874  return(true);
14875 }
14876 
14877 // ---------------------------------------------------------------------------
14878 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
14880 {
14881  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
14882 }
14883 
14884 // ---------------------------------------------------------------------------
14885 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/dsc/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
14887 {
14888  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
14889  (AVEntry.Command == "cdt") || (AVEntry.Command == "dsc") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
14890  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
14891 }
14892 
14893 // ---------------------------------------------------------------------------
14894 
14895 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
14896 {
14897  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
14898  if(HeadCode.Length() > 4) // ignore otherwise
14899  {
14900  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
14901  }
14902  Utilities->CallLogPop(1593);
14903 }
14904 
14905 // ---------------------------------------------------------------------------
14906 
14907 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
14908 {
14909  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
14910  SecondHeadCode);
14911  int ForwardCount = 0;
14912  int ReverseCount = 0;
14913 
14914  if(MainHeadCode == SecondHeadCode)
14915  {
14916  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
14917  TrainDataVector.clear();
14918  Utilities->CallLogPop(1594);
14919  return(false);
14920  }
14921  // forward check
14922  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14923  {
14924  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14925  if(TDEntry.HeadCode == MainHeadCode)
14926  {
14927  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14928  {
14929  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14930  if(AVEntry.OtherHeadCode == SecondHeadCode)
14931  {
14932  ForwardCount++;
14933  }
14934  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
14935  // need own check in case both 'Other' & 'NonRepeating' have same headcode
14936  {
14937  ForwardCount++;
14938  }
14939  }
14940  }
14941  }
14942  if(ForwardCount == 0)
14943  // this is an exception because the headcodes are selected in the same order as the forward check
14944  {
14945  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
14946  }
14947  if(ForwardCount > 2)
14948  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
14949  {
14950  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
14951  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
14952  TrainDataVector.clear();
14953  Utilities->CallLogPop(1587);
14954  return(false);
14955  }
14956  // reverse check
14957  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14958  {
14959  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14960  if(TDEntry.HeadCode == SecondHeadCode)
14961  {
14962  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14963  {
14964  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14965  if(AVEntry.OtherHeadCode == MainHeadCode)
14966  {
14967  ReverseCount++;
14968  }
14969  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14970  {
14971  ReverseCount++;
14972  }
14973  }
14974  }
14975  }
14976 
14977  if(ReverseCount == 0)
14978  {
14979  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
14980  TrainDataVector.clear();
14981  Utilities->CallLogPop(1588);
14982  return(false);
14983  }
14984  if(ReverseCount > 2)
14985  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
14986  {
14987  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
14988  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
14989  TrainDataVector.clear();
14990  Utilities->CallLogPop(1589);
14991  return(false);
14992  }
14993  if(ForwardCount != ReverseCount)
14994  {
14995  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
14996  " than the other way round");
14997  TrainDataVector.clear();
14998  Utilities->CallLogPop(1610);
14999  return(false);
15000  }
15001  Utilities->CallLogPop(1590);
15002  return(true);
15003 }
15004 
15005 // ---------------------------------------------------------------------------
15006 
15007 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool SetDataAndCheckLocations, bool GiveMessages)
15008 /* Return false for no find or more than one find, check correct types of link
15009  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
15010  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
15011  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
15012  Then do the same in reverse.
15013  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
15014  if main is Fns other must be Sns; if main is jbo other must be Fjo.
15015  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
15016  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
15017  for Sfs & Sns services. Finally check the repeat entries if present are consistent
15018 
15019  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
15020  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
15021 
15022  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
15023 */
15024 
15025 {
15026  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
15027  int ForwardCount = 0;
15028  int ReverseCount = 0;
15029  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
15030  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
15031  TTrainDataEntry *MainTrainDataPtr = 0;
15032  TTrainDataEntry *OtherTrainDataPtr = 0;
15033 
15034  // forward check
15035  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15036  {
15037  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15038  if(TDEntry.HeadCode == MainHeadCode)
15039  {
15040  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15041  {
15042  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15043  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
15044  {
15045  if(AVEntry.OtherHeadCode == OtherHeadCode)
15046  {
15047  MainTrainDataPtr = &TrainDataVector.at(x);
15048  ForwardEntryPtr = &AVEntry;
15049  ForwardCount++;
15050  ForwardTDVectorNumber = x;
15051  }
15052  }
15053  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
15054  (AVEntry.Command == "Frh-sh")))
15055  {
15056  if(AVEntry.OtherHeadCode == OtherHeadCode)
15057  {
15058  MainTrainDataPtr = &TrainDataVector.at(x);
15059  ForwardEntryPtr = &AVEntry;
15060  ForwardCount++;
15061  ForwardTDVectorNumber = x;
15062  }
15063  }
15064  }
15065  }
15066  }
15067  if(ForwardCount == 0)
15068  // this is an exception because the headcodes are selected in the same order as the forward check
15069  {
15070  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
15071  }
15072  if(ForwardCount > 1)
15073  {
15074  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
15075  MainHeadCode);
15076  TrainDataVector.clear();
15077  Utilities->CallLogPop(836);
15078  return(false);
15079  }
15080  // reverse check
15081  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15082  {
15083  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15084  if(TDEntry.HeadCode == OtherHeadCode)
15085  {
15086  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15087  {
15088  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15089  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
15090  {
15091  if(AVEntry.OtherHeadCode == MainHeadCode)
15092  {
15093  OtherTrainDataPtr = &TrainDataVector.at(x);
15094  ReverseCount++;
15095  ReverseEntryPtr = &AVEntry;
15096  ReverseTDVectorNumber = x;
15097  }
15098  }
15099  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
15100  {
15101  if(AVEntry.OtherHeadCode == MainHeadCode)
15102  {
15103  OtherTrainDataPtr = &TrainDataVector.at(x);
15104  ReverseCount++;
15105  ReverseEntryPtr = &AVEntry;
15106  ReverseTDVectorNumber = x;
15107  }
15108  }
15109  }
15110  }
15111  }
15112 
15113  if(ReverseCount == 0)
15114  {
15115  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
15116  TrainDataVector.clear();
15117  Utilities->CallLogPop(837);
15118  return(false);
15119  }
15120  if(ReverseCount > 1)
15121  {
15122  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
15123  OtherHeadCode);
15124  TrainDataVector.clear();
15125  Utilities->CallLogPop(838);
15126  return(false);
15127  }
15128  // these will all be false for !Shuttle
15129  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
15130  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
15131  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
15132  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
15133 
15134  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
15135  {
15136  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
15137  TrainDataVector.clear();
15138  Utilities->CallLogPop(1058);
15139  return(false);
15140  }
15141  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
15142  {
15143  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
15144  TrainDataVector.clear();
15145  Utilities->CallLogPop(1059);
15146  return(false);
15147  }
15148  if(SetDataAndCheckLocations)
15149  {
15150  if(ForwardEntryPtr->LocationName == "")
15151  {
15152  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
15153  ". One or other service does not have a location set");
15154  TrainDataVector.clear();
15155  Utilities->CallLogPop(526);
15156  return(false);
15157  }
15158  if(ReverseEntryPtr->LocationName == "")
15159  {
15160  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
15161  ". One or other service does not have a location set");
15162  TrainDataVector.clear();
15163  Utilities->CallLogPop(527);
15164  return(false);
15165  }
15166  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
15167  {
15168  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
15169  " is at a different location to the referencing train " + MainHeadCode);
15170  TrainDataVector.clear();
15171  Utilities->CallLogPop(842);
15172  return(false);
15173  }
15174  }
15175  // ignore shuttle repeat links for first time check
15176  if(!Shuttle)
15177  {
15178  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
15179  {
15180  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
15181  " has a different event time to the referencing train " + MainHeadCode);
15182  TrainDataVector.clear();
15183  Utilities->CallLogPop(525);
15184  return(false);
15185  }
15186  }
15187  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
15188  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
15189  if(ForwardShuttleStart && ReverseShuttleFinish)
15190  // Shuttle must be true if these are true
15191  {
15192  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
15193  {
15194  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
15195  " first repeat restart time not consistent with finish service " + OtherHeadCode);
15196  TrainDataVector.clear();
15197  Utilities->CallLogPop(1055);
15198  return(false);
15199  }
15200  }
15201  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
15202  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
15203  {
15204  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15205  {
15206  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
15207  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15208  TrainDataVector.clear();
15209  Utilities->CallLogPop(528);
15210  return(false);
15211  }
15212  }
15213  if(ReverseEntryPtr->Command == "Fjo")
15214  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
15215  {
15216  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15217  {
15218  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
15219  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15220  TrainDataVector.clear();
15221  Utilities->CallLogPop(862);
15222  return(false);
15223  }
15224  }
15225  if(ReverseEntryPtr->Command == "Fns")
15226  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
15227  {
15228  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
15229  {
15230  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
15231  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
15232  TrainDataVector.clear();
15233  Utilities->CallLogPop(529);
15234  return(false);
15235  }
15236  }
15237  if(ForwardEntryPtr->Command == "Sfs")
15238  {
15239  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
15240  {
15241  SecondPassMessage(GiveMessages,
15242  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
15243  MainHeadCode);
15244  TrainDataVector.clear();
15245  Utilities->CallLogPop(530);
15246  return(false);
15247  }
15248  }
15249  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
15250  {
15251  if(ReverseEntryPtr->Command != "Sfs")
15252  {
15253  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
15254  MainHeadCode);
15255  TrainDataVector.clear();
15256  Utilities->CallLogPop(839);
15257  return(false);
15258  }
15259  else
15260  {
15261  if(SetDataAndCheckLocations)
15262  {
15263  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
15264  {
15265  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
15266  TrainDataVector.clear();
15267  Utilities->CallLogPop(849);
15268  return(false);
15269  }
15270  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
15271  {
15272  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
15273  TrainDataVector.clear();
15274  Utilities->CallLogPop(850);
15275  return(false);
15276  }
15277 //determine whether LocationName is a station or non-station
15278  bool StationLocation = false;
15279  for(TTrack::TTrackVectorIterator TEIt = Track->InactiveTrackVector.begin(); TEIt != Track->InactiveTrackVector.end(); TEIt++)
15280  {
15281  if(TEIt->LocationName == ForwardEntryPtr->LocationName)
15282  {
15283  if(TEIt->TrackType != NamedNonStationLocation)
15284  {
15285  StationLocation = true;
15286  }
15287  }
15288  }
15289  if(StationLocation)
15290  {
15291  if(!(Track->OneStationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
15292  {
15293  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
15294  TrainDataVector.clear();
15295  Utilities->CallLogPop(846);
15296  return(false);
15297  }
15298  }
15299  else
15300  {
15301  if(!(Track->OneNonStationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
15302  {
15303  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
15304  TrainDataVector.clear();
15305  Utilities->CallLogPop(2660);
15306  return(false);
15307  }
15308  }
15309  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15310  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15311  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
15312  {
15313  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
15314  }
15315  }
15316  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
15317  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15318  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15319  }
15320  }
15321  if(ForwardEntryPtr->Command == "Sns")
15322  {
15323  if(ReverseEntryPtr->Command != "Fns")
15324  {
15325  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
15326  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
15327  TrainDataVector.clear();
15328  Utilities->CallLogPop(531);
15329  return(false);
15330  }
15331  }
15332  if(ForwardEntryPtr->Command == "Fns")
15333  {
15334  if(ReverseEntryPtr->Command != "Sns")
15335  {
15336  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
15337  " and forms a new service with headcode " + OtherHeadCode);
15338  TrainDataVector.clear();
15339  Utilities->CallLogPop(840);
15340  return(false);
15341  }
15342  else
15343  {
15344  if(SetDataAndCheckLocations)
15345  {
15346  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15347  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15348  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
15349  {
15350  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
15351  }
15352  }
15353  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15354  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
15355  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
15356  }
15357  }
15358  if(ForwardEntryPtr->Command == "jbo")
15359  {
15360  if(ReverseEntryPtr->Command != "Fjo")
15361  {
15362  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
15363  " and is joined by a train with headcode " + OtherHeadCode);
15364  TrainDataVector.clear();
15365  Utilities->CallLogPop(841);
15366  return(false);
15367  }
15368  }
15369  if(ForwardEntryPtr->Command == "Fjo")
15370  {
15371  if(ReverseEntryPtr->Command != "jbo")
15372  {
15373  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
15374  " and joins a train with headcode " + OtherHeadCode);
15375  TrainDataVector.clear();
15376  Utilities->CallLogPop(532);
15377  return(false);
15378  }
15379  else
15380  {
15381  if(SetDataAndCheckLocations)
15382  {
15383  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15384  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15385  }
15386 /*
15387  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
15388  {
15389  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed; //this should only be set when the trains join
15390  } not when internal timetable being compiled. Dropped at v2.15.0 when Brent Mackie reported error on 18/02/23 via discord
15391 
15392  //added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a
15393  //zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
15394  //notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the
15395  //'joined by' train's max speed is less.
15396 */
15397  }
15398  }
15399  if(ForwardShuttleStart)
15400  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
15401  {
15402  if(!ReverseShuttleFinish)
15403  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
15404  {
15405  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
15406  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
15407  TrainDataVector.clear();
15408  Utilities->CallLogPop(1056);
15409  return(false);
15410  }
15411  }
15412  if(ReverseShuttleStart)
15413  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
15414  {
15415  if(!ForwardShuttleFinish)
15416  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
15417  {
15418  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
15419  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
15420  TrainDataVector.clear();
15421  Utilities->CallLogPop(1057);
15422  return(false);
15423  }
15424  else
15425  {
15426  if(SetDataAndCheckLocations)
15427  {
15428  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
15429  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
15430  }
15431 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
15432  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
15433 */
15434  }
15435  }
15436  // check repeat information consistent if present
15437  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
15438  // and those not accessed here
15439 
15440  // still need to check the non-repeating links and that they have no repeats - do that outside this function
15441  bool MainRepeat = false, OtherRepeat = false;
15442  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
15443 
15444  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
15445  {
15446  MainRepeat = true;
15447  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
15448  }
15449  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
15450  {
15451  OtherRepeat = true;
15452  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
15453  }
15454  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
15455  {
15456  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
15457  " and the associated train with headcode " + OtherHeadCode);
15458  TrainDataVector.clear();
15459  Utilities->CallLogPop(844);
15460  return(false);
15461  }
15462  if(MainRepeat && OtherRepeat)
15463  {
15464  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
15465  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
15466  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
15467  {
15468  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
15469  " and the associated train with headcode " + OtherHeadCode);
15470  TrainDataVector.clear();
15471  Utilities->CallLogPop(845);
15472  return(false);
15473  }
15474  }
15475  Utilities->CallLogPop(863);
15476  return(true);
15477 }
15478 
15479 // ---------------------------------------------------------------------------
15480 
15481 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
15482 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
15483 {
15484  // strip spaces from extreme ends of input
15485  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
15486  if(Input == "")
15487  {
15488  Utilities->CallLogPop(856);
15489  return;
15490  }
15491  while(Input[1] == ' ')
15492  {
15493  if(Input.Length() > 1)
15494  {
15495  Input = Input.SubString(2, Input.Length() - 1);
15496  }
15497  else
15498  {
15499  Input = "";
15500  Utilities->CallLogPop(857);
15501  return;
15502  }
15503  }
15504  if(Input == "")
15505  {
15506  Utilities->CallLogPop(858);
15507  return;
15508  }
15509  while(Input[Input.Length()] == ' ')
15510  {
15511  if(Input.Length() > 1)
15512  {
15513  Input = Input.SubString(1, Input.Length() - 1);
15514  }
15515  else
15516  {
15517  Input = "";
15518  Utilities->CallLogPop(859);
15519  return;
15520  }
15521  }
15522  // now strip spaces immediately after all commas and semicolons within the text
15523  AnsiString Output = "";
15524  bool DelimiterFound = false;
15525 
15526  for(int x = 1; x < Input.Length() + 1; x++)
15527  {
15528  if(DelimiterFound)
15529  {
15530  if(Input[x] == ' ')
15531  {
15532  continue;
15533  }
15534  }
15535  if((Input[x] != ',') && (Input[x] != ';'))
15536  {
15537  DelimiterFound = false;
15538  Output = Output + Input[x];
15539  }
15540  else
15541  {
15542  DelimiterFound = true;
15543  Output = Output + Input[x];
15544  }
15545  }
15546  if(Output == "")
15547  {
15548  Input = "";
15549  Utilities->CallLogPop(860);
15550  return;
15551  }
15552  // now strip spaces immediately before all commas and semicolons within the text
15553  Input = Output;
15554  Output = "";
15555  DelimiterFound = false;
15556  for(int x = Input.Length(); x > 0; x--)
15557  {
15558  if(DelimiterFound)
15559  {
15560  if(Input[x] == ' ')
15561  {
15562  continue;
15563  }
15564  }
15565  if((Input[x] != ',') && (Input[x] != ';'))
15566  {
15567  DelimiterFound = false;
15568  Output = AnsiString(Input[x]) + Output;
15569  }
15570  else
15571  {
15572  DelimiterFound = true;
15573  Output = AnsiString(Input[x]) + Output;
15574  }
15575  }
15576  Input = Output;
15577  Utilities->CallLogPop(861);
15578 }
15579 
15580 // ---------------------------------------------------------------------------
15581 
15582 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName) //this is the original version re-instated at v2.15.0 Beta6
15583 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
15584 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
15585 // a signaller control entry & speed is zero or it is followed immediately by Frh, Fjo, Fns or F-nshs (allows empty stock pickup).
15586 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
15587 // are done in this function, they must be done elsewhere.
15588 //a starting speed > 0 always returns false
15589 {
15590  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
15591  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
15592  LocationName = "";
15593  if(TDEntry.StartSpeed > 0)
15594  {
15595  Utilities->CallLogPop(1784);
15596  return(false);
15597  }
15598  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
15599  {
15600  throw Exception("Error, first event not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
15601  }
15603  {
15604  Utilities->CallLogPop(852);
15605  return(false);
15606  }
15607  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
15608  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
15609 
15610  if(LocRear != "")
15611  {
15612  LocationName = LocRear;
15613  }
15614  else
15615  {
15616  LocationName = LocFront;
15617  }
15618  if(LocationName == "")
15619  {
15620  Utilities->CallLogPop(1036);
15621  return(false);
15622  }
15623  if(AVEntry0.SignallerControl)
15624  {
15625  Utilities->CallLogPop(1773);
15626  return(true);
15627  }
15628 // here if not a signaller start entry so must be at least one more entry, and it must be at the same location as the Snt to be located
15629 
15630 //Ok Not ok continue
15631 
15632 //Frh if Snt Frh-sh cdt
15633 //Fns if Snt Fns-sh fsp or rsp
15634 //Fjo if Snt TimeTimeLoc jbo
15635 //F-nshs if Snt pas dsc
15636 //TimeLoc dep Fer
15637 // TimeLoc arr
15638 
15639  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
15640  {
15641  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
15642  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
15643  {
15644  Utilities->CallLogPop(1037);
15645  return(true);
15646  }
15647  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
15648  {
15649  Utilities->CallLogPop(2442);
15650  return(true);
15651  }
15652  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
15653  {
15654  Utilities->CallLogPop(2438);
15655  return(false);
15656  }
15657  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
15658  {
15659  Utilities->CallLogPop(854);
15660  return(false);
15661  }
15662  if((AVEntry.Command == "cdt") || (AVEntry.Command == "dsc") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
15663  {
15664  continue;
15665  }
15666  }
15667  Utilities->CallLogPop(855);
15668  return(false);
15669 
15670 }
15671 
15672 // ---------------------------------------------------------------------------
15673 
15674 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
15675 {
15676  // checks that the new train start elements are valid - both exist & are connected, and that not
15677  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg) <--dropped at v2.18.0
15678  // & not starting with front on a continuation
15679  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
15680  int RearPosition = 0, FrontPosition = 0; // RearExitPos = 0; //dropped at v2.18.0
15681 
15682  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
15683  if(RearPosition < 0)
15684  // error message given in GetTrackVectorPositionFromString
15685  {
15686  Utilities->CallLogPop(759);
15687  return(false);
15688  }
15689  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
15690  if(FrontPosition < 0)
15691  // error message given in GetTrackVectorPositionFromString
15692  {
15693  Utilities->CallLogPop(760);
15694  return(false);
15695  }
15696  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
15697  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
15698 // TTrackType RearType = RearTrackElement.TrackType; //dropped at v2.18.0
15699  TTrackType FrontType = FrontTrackElement.TrackType;
15700 
15701  // check front & rear connected
15702  for(int x = 0; x < 4; x++)
15703  {
15704  if(RearTrackElement.Conn[x] == FrontPosition)
15705  {
15706 // RearExitPos = x; //dropped at v2.18.0
15707  break;
15708  }
15709  if(x == 3) //if it gets here & not already found then not connected
15710  {
15711  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
15712  Utilities->CallLogPop(762);
15713  return(false);
15714  }
15715  }
15716  // check not starting with front on a continuation
15717  if(FrontType == Continuation)
15718  {
15719  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
15720  Utilities->CallLogPop(937);
15721  return(false);
15722  }
15723  // check not starting on a level crossing
15724  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
15725  {
15726  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
15727  Utilities->CallLogPop(1951);
15728  return(false);
15729  }
15730  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
15731  {
15732  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
15733  Utilities->CallLogPop(1952);
15734  return(false);
15735  }
15736  // check if trying to start on diverging leg of points - allowed at v2.18.0, checks done during operation
15737 /*
15738  if((RearType == Points) && (RearExitPos == 3))
15739  {
15740  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
15741  Utilities->CallLogPop(936);
15742  return(false);
15743  }
15744 
15745  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
15746  {
15747  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
15748  Utilities->CallLogPop(1808);
15749  return(false);
15750  }
15751 */
15752  Utilities->CallLogPop(905);
15753  return(true);
15754 }
15755 
15756 // ---------------------------------------------------------------------------
15757 
15758 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
15759 // Rear & front element validity already checked in CheckStartPositionValidity
15760 // This checks for points in correct orientation, no train at start position and not starting on a locked route
15761 {
15762  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
15763  AnsiString(RearExitPos));
15764  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
15765 
15766  if(RearTrackElement.TrackType == Continuation)
15767  {
15768  EventType = FailTrainEntry;
15769  }
15770  else
15771  {
15772  EventType = FailCreateTrain;
15773  }
15774  int FrontPosition = RearTrackElement.Conn[RearExitPos];
15775  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
15776  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
15777  TTrackType RearType = RearTrackElement.TrackType;
15778  TTrackType FrontType = FrontTrackElement.TrackType;
15779  AnsiString RearName, FrontName;
15780 
15781  if(RearTrackElement.ActiveTrackElementName != "")
15782  {
15783  RearName = RearTrackElement.ActiveTrackElementName;
15784  }
15785  else
15786  {
15787  RearName = RearTrackElement.ElementID;
15788  }
15789  if(FrontTrackElement.ActiveTrackElementName != "")
15790  {
15791  FrontName = FrontTrackElement.ActiveTrackElementName;
15792  }
15793  else
15794  {
15795  FrontName = FrontTrackElement.ElementID;
15796  }
15797  TPrefDirElement PrefDirElement; // needed for next function but not used
15798  int LockedVectorNumber; // needed for next function but not used
15799 
15800  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
15801  {
15802  if(ReportFlag)
15803  {
15804  if(EventType == FailCreateTrain)
15805  {
15806  EventType = FailCreateLockedRoute;
15807  }
15808  else
15809  {
15810  EventType = FailEnterLockedRoute;
15811  }
15812  LogActionError(47, HeadCode, "", EventType, FrontName);
15813  }
15814  Utilities->CallLogPop(940);
15815  return(false);
15816  }
15817  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
15818  {
15819  if(ReportFlag)
15820  {
15821  if(EventType == FailCreateTrain)
15822  {
15823  EventType = FailCreateLockedRoute;
15824  }
15825  else
15826  {
15827  EventType = FailEnterLockedRoute;
15828  }
15829  LogActionError(48, HeadCode, "", EventType, RearName);
15830  }
15831  Utilities->CallLogPop(1809);
15832  return(false);
15833  }
15834  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
15835  {
15836  if(ReportFlag)
15837  {
15838  LogActionError(27, HeadCode, "", EventType, RearName);
15839  }
15840  Utilities->CallLogPop(1810);
15841  return(false);
15842  }
15843  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
15844  {
15845  if(ReportFlag)
15846  {
15847  if(EventType == FailCreateTrain)
15848  {
15849  LogActionError(28, HeadCode, "", EventType, FrontName);
15850  }
15851  else
15852  {
15853  LogActionError(43, HeadCode, "", EventType, RearName);
15854  }
15855  }
15856  Utilities->CallLogPop(941);
15857  return(false);
15858  }
15859  if(RearType == Bridge)
15860  {
15861  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15862  {
15863  if(ReportFlag)
15864  {
15865  LogActionError(29, HeadCode, "", EventType, RearName);
15866  }
15867  Utilities->CallLogPop(942);
15868  return(false);
15869  }
15870  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15871  {
15872  if(ReportFlag)
15873  {
15874  LogActionError(30, HeadCode, "", EventType, RearName);
15875  }
15876  Utilities->CallLogPop(943);
15877  return(false);
15878  }
15879  }
15880  if(FrontType == Bridge)
15881  {
15882  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit23 > -1))
15883  {
15884  if(ReportFlag)
15885  {
15886  if(EventType == FailCreateTrain)
15887  {
15888  LogActionError(31, HeadCode, "", EventType, FrontName);
15889  }
15890  else
15891  {
15892  LogActionError(44, HeadCode, "", EventType, RearName);
15893  }
15894  }
15895  Utilities->CallLogPop(944);
15896  return(false);
15897  }
15898  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeOrFailedPointOrigSpeedLimit01 > -1))
15899  {
15900  if(ReportFlag)
15901  {
15902  if(EventType == FailCreateTrain)
15903  {
15904  LogActionError(45, HeadCode, "", EventType, FrontName);
15905  }
15906  else
15907  {
15908  LogActionError(46, HeadCode, "", EventType, RearName);
15909  }
15910  }
15911  Utilities->CallLogPop(945);
15912  return(false);
15913  }
15914  }
15915  EventType = FailCreatePoints; //modified at v2.18.0 so only fails if starting positions conflict with point attribute
15916  if(RearType == Points)
15917  {
15918  if(((RearTrackElement.Attribute == 1) && (RearExitPos == 1)) || ((RearTrackElement.Attribute == 0) && (RearExitPos == 3)))
15919  {
15920  if(ReportFlag)
15921  {
15922  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
15923  StopTTClockMessage(157, HeadCode + " can't be created, points set wrongly at " + RearName);
15924  }
15925  Utilities->CallLogPop(933);
15926  return(false);
15927  }
15928  }
15929  if(FrontType == Points)
15930  {
15931  if(((FrontTrackElement.Attribute == 1) && (FrontEntryPos == 1)) || ((FrontTrackElement.Attribute == 0) && (FrontEntryPos == 3)))
15932  {
15933  if(ReportFlag)
15934  {
15935  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
15936  StopTTClockMessage(158, HeadCode + " can't be created, points set wrongly at " + RearName);
15937  }
15938  Utilities->CallLogPop(934);
15939  return(false);
15940  }
15941  }
15942 
15943  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
15944  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
15945  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
15946  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
15947  int RouteNumber; //not used
15948  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
15949  {
15950  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
15951  {
15952  EventType = FailEntryRouteSetAgainst;
15953  if(ReportFlag)
15954  {
15955  LogActionError(63, HeadCode, "", EventType, RearName);
15956  }
15957  Utilities->CallLogPop(2317);
15958  return(false);
15959  }
15960  }
15961  Utilities->CallLogPop(939);
15962  return(true);
15963 }
15964 
15965 // ---------------------------------------------------------------------------
15966 
15967 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
15968 {
15969  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
15970  "," + AnsiString(IncDigits));
15971  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
15972  {
15973  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
15974  }
15975  if(!Last2CharactersBothDigits(2, BaseHeadCode))
15976  {
15977  Utilities->CallLogPop(1893);
15978  return(BaseHeadCode);
15979  }
15980  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
15981  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
15982 
15983  while(NextRepeatDigits >= 100)
15984  {
15985  NextRepeatDigits -= 100; // rolls over after 99
15986  }
15987  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
15988 
15989  if(NextRepeatDigitsStr.Length() < 2)
15990  {
15991  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
15992  }
15993  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
15994 
15995  Utilities->CallLogPop(1365);
15996  return(NextRepeatHeadCode);
15997 }
15998 
15999 // ---------------------------------------------------------------------------
16000 
16001 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
16002 {
16003  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
16004  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
16005  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
16006  Utilities->CallLogPop(1366);
16007  return(NextRepeatTime);
16008 }
16009 
16010 // ---------------------------------------------------------------------------
16011 
16012 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
16013 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
16014 {
16015  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
16016  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
16017  int ForwardSecs = int(double(ForwardEventTime) * 86400);
16018  int ReverseSecs = int(double(ReverseEventTime) * 86400);
16019  int RepeatSecs = RepeatMinutes * 60;
16020 
16021  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
16022  {
16023  Utilities->CallLogPop(1367);
16024  return(false);
16025  }
16026  else
16027  {
16028  Utilities->CallLogPop(1368);
16029  return(true);
16030  }
16031 }
16032 
16033 // ---------------------------------------------------------------------------
16034 
16035 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool SetDataAndCheckLocations, bool GiveMessages)
16036 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent, set links if SetDataAndCheckLocations true
16037 
16038 /* Double crosslink (shuttle) table:
16039 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
16040  Code ShuttleLink- EntryPtr ShuttleLink-
16041  HeadCode EntryPtr
16042 
16043 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
16044 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
16045 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
16046 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
16047 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
16048 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
16049 
16050 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
16051 */
16052 
16053 {
16054  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
16055  NonRepeatingHeadCode);
16056  int ForwardCount = 0;
16057  int ReverseCount = 0;
16058  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
16059  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
16060  // Forward corresponds to Main, Reverse to Other
16061  TTrainDataEntry *MainTrainDataPtr = 0;
16062  TTrainDataEntry *OtherTrainDataPtr = 0;
16063 
16064  // forward check
16065  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16066  {
16067  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16068  if(TDEntry.HeadCode == MainHeadCode)
16069  {
16070  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16071  {
16072  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16073  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
16074  {
16075  MainTrainDataPtr = &TrainDataVector.at(x);
16076  ForwardEntryPtr = &AVEntry;
16077  ForwardCount++;
16078  ForwardTDVectorNumber = x;
16079  }
16080  }
16081  }
16082  }
16083  if(ForwardCount == 0)
16084  // this is an exception because the headcodes are selected in the same order as the forward check
16085  {
16086  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
16087  }
16088  if(ForwardCount > 1)
16089  {
16090  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
16091  MainHeadCode);
16092  TrainDataVector.clear();
16093  Utilities->CallLogPop(1061);
16094  return(false);
16095  }
16096  // reverse check
16097  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16098  {
16099  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16100  if(TDEntry.HeadCode == NonRepeatingHeadCode)
16101  {
16102  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16103  {
16104  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16105  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
16106  {
16107  OtherTrainDataPtr = &TrainDataVector.at(x);
16108  ReverseCount++;
16109  ReverseEntryPtr = &AVEntry;
16110  ReverseTDVectorNumber = x;
16111  }
16112  }
16113  }
16114  }
16115 
16116  if(ReverseCount == 0)
16117  {
16118  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
16119  TrainDataVector.clear();
16120  Utilities->CallLogPop(1062);
16121  return(false);
16122  }
16123  if(ReverseCount > 1)
16124  {
16125  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
16126  NonRepeatingHeadCode);
16127  TrainDataVector.clear();
16128  Utilities->CallLogPop(1063);
16129  return(false);
16130  }
16131  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
16132  {
16133  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
16134  TrainDataVector.clear();
16135  Utilities->CallLogPop(1064);
16136  return(false);
16137  }
16138  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
16139  {
16140  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
16141  TrainDataVector.clear();
16142  Utilities->CallLogPop(1065);
16143  return(false);
16144  }
16145  if(SetDataAndCheckLocations)
16146  {
16147  if(ForwardEntryPtr->LocationName == "")
16148  {
16149  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
16150  ". One or other service does not have a location set");
16151  TrainDataVector.clear();
16152  Utilities->CallLogPop(1066);
16153  return(false);
16154  }
16155  if(ReverseEntryPtr->LocationName == "")
16156  {
16157  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
16158  ". One or other service does not have a location set");
16159  TrainDataVector.clear();
16160  Utilities->CallLogPop(1067);
16161  return(false);
16162  }
16163  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
16164  {
16165  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
16166  " is at a different location to the referencing train " + MainHeadCode);
16167  TrainDataVector.clear();
16168  Utilities->CallLogPop(1068);
16169  return(false);
16170  }
16171  }
16172  if(ForwardEntryPtr->Command == "F-nshs")
16173  // i.e. the non repeating link into the shuttle service
16174  {
16175  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
16176  {
16177  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
16178  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
16179  TrainDataVector.clear();
16180  Utilities->CallLogPop(1069);
16181  return(false);
16182  }
16183  }
16184  if(ForwardEntryPtr->Command == "Fns-sh")
16185  // i.e. the non repeating link out from the shuttle service
16186  {
16187  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
16188  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
16189  {
16190  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
16191  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
16192  TrainDataVector.clear();
16193  Utilities->CallLogPop(1070);
16194  return(false);
16195  }
16196  }
16197  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
16198  // i.e. a non repeating link to or from the shuttle service
16199  {
16200  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
16201  {
16202  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
16203  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
16204  TrainDataVector.clear();
16205  Utilities->CallLogPop(1071);
16206  return(false);
16207  }
16208  }
16209 /* it's allowed to have a different description
16210  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
16211  {
16212  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
16213  {
16214  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
16215  TrainDataVector.clear();
16216  Utilities->CallLogPop(1072);
16217  return false;
16218  }
16219  }
16220 */
16221  if(ForwardEntryPtr->Command == "Sns-sh")
16222  {
16223  if(ReverseEntryPtr->Command != "F-nshs")
16224  {
16225  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
16226  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
16227  TrainDataVector.clear();
16228  Utilities->CallLogPop(1073);
16229  return(false);
16230  }
16231  }
16232  if(ForwardEntryPtr->Command == "F-nshs")
16233  {
16234  if(ReverseEntryPtr->Command != "Sns-sh")
16235  {
16236  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
16237  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
16238  TrainDataVector.clear();
16239  Utilities->CallLogPop(1074);
16240  return(false);
16241  }
16242  else
16243  {
16244  if(SetDataAndCheckLocations)
16245  {
16246  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
16247  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
16248  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
16249  {
16250  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
16251  }
16252  }
16253  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16254  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
16255  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16256  }
16257  }
16258  if(ForwardEntryPtr->Command == "Sns-fsh")
16259  {
16260  if(ReverseEntryPtr->Command != "Fns-sh")
16261  {
16262  SecondPassMessage(GiveMessages,
16263  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
16264  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
16265  TrainDataVector.clear();
16266  Utilities->CallLogPop(1075);
16267  return(false);
16268  }
16269  }
16270  if(ForwardEntryPtr->Command == "Fns-sh")
16271  {
16272  if(ReverseEntryPtr->Command != "Sns-fsh")
16273  {
16274  SecondPassMessage(GiveMessages,
16275  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
16276  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
16277  TrainDataVector.clear();
16278  Utilities->CallLogPop(1076);
16279  return(false);
16280  }
16281  else
16282  {
16283  if(SetDataAndCheckLocations)
16284  {
16285  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
16286  // links to the non-repeating non-shuttle linked service
16287  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
16288  // needed for creating formatted timetable
16289  if(OtherTrainDataPtr->FixedDescription == "") //name changed at v2.16.1
16290  {
16291  OtherTrainDataPtr->FixedDescription = MainTrainDataPtr->FixedDescription;
16292  }
16293  }
16294  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
16295  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
16296  }
16297  }
16298  Utilities->CallLogPop(1077);
16299  return(true);
16300 }
16301 
16302 // ---------------------------------------------------------------------------
16303 
16304 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
16305 // Forward train is the finish shuttle entry 'Fns-sh'.
16306 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
16307 {
16308  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
16309  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
16310  int ForwardSecs = int(double(ForwardEventTime) * 86400);
16311  int ReverseSecs = int(double(ReverseEventTime) * 86400);
16312  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
16313 
16314  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
16315  {
16316  Utilities->CallLogPop(1369);
16317  return(false);
16318  }
16319  else
16320  {
16321  Utilities->CallLogPop(1370);
16322  return(true);
16323  }
16324 }
16325 
16326 // ---------------------------------------------------------------------------
16327 
16328 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
16329 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
16330 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
16331 // don't ever need to and as designed would skip repeats.
16332 
16333 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
16334 {
16335  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
16336  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
16337  {
16338  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
16339  }
16340  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
16341  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
16342  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
16343 
16344  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
16345  {
16346  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
16347  TrainDataVector.clear();
16348  Utilities->CallLogPop(1091);
16349  return(false);
16350  }
16351  while(LastActionCommand == "Fns")
16352  {
16353  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
16354  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
16355  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
16356  {
16357  SecondPassMessage(GiveMessages,
16358  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
16359  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
16360  TrainDataVector.clear();
16361  Utilities->CallLogPop(1092);
16362  return(false);
16363  }
16364  }
16365  // exit the 'while' with LastActionCommand FSH-XX
16366  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
16367  {
16368  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
16369  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
16370  ". The linking of two or more shuttles is not permitted.");
16371  TrainDataVector.clear();
16372  Utilities->CallLogPop(1093);
16373  return(false);
16374  }
16375  Utilities->CallLogPop(1094);
16376  return(true);
16377 }
16378 
16379 // ---------------------------------------------------------------------------
16380 
16381 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
16382 {
16383  if(!GiveMessages)
16384  {
16385  return;
16386  }
16387  // if(ServiceReference == "") ShowMessage(Message);
16388  if(!CheckHeadCodeValidity(12, false, ServiceReference))
16389  {
16390  ShowMessage(ServiceReference + " (not a valid service ref.): " + Message); //amended at v2.15.1 to give information on 'service' so can find it in lh list
16391  }
16392  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
16393  // false means don't give messages within the function
16394  else
16395  {
16396  ShowMessage("Service " + ServiceReference + ": " + Message);
16397  }
16398 }
16399 
16400 // ---------------------------------------------------------------------------
16401 
16402 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
16403 {
16404  if(!GiveMessages)
16405  {
16406  return;
16407  }
16408  ShowMessage(Message);
16409 }
16410 
16411 // ---------------------------------------------------------------------------
16412 
16413 AnsiString TTrainController::MinsToAnsiTime(int Input) //added at v2.15.0
16414 {
16415  TrainController->LogEvent("MinsToAnsiTime");
16416  Utilities->CallLog.push_back(Utilities->TimeStamp() + ",MinsToAnsiTime," + Input);
16417  int Mins = Input, Hrs = 0;
16418  while(Mins > 59)
16419  {
16420  Mins -= 60;
16421  Hrs++;
16422  }
16423  AnsiString AnsiMins = AnsiString(Mins);
16424  if(AnsiMins.Length() == 1)
16425  {
16426  AnsiMins = "0" + AnsiMins;
16427  }
16428  AnsiString AnsiHrs = AnsiString(Hrs);
16429  if(AnsiHrs.Length() == 1)
16430  {
16431  AnsiHrs = "0" + AnsiHrs;
16432  }
16433  Utilities->CallLogPop(2577);
16434  return(AnsiHrs + ':' + AnsiMins);
16435 }
16436 
16437 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
16438 // ---------------------------------------------------------------------------
16439 
16440 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
16441 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
16442 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
16443 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
16444 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
16445 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set against start position 57-N5
16446 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
16447 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
16448 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
16449 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
16450 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
16451 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road, please move it if possible
16452 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
16453 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
16454 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
16455 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
16456 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
16457 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
16458 // FailMissedDSC: 06:00:10: ERROR: 2F43 failed to change its description at Essex Road
16459 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
16460 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
16461 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
16462 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
16463 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
16464 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
16465 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
16466 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
16467 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
16468 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
16469 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
16470 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
16471 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
16472 {
16473  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
16474  AnsiString(ActionEventType) + "," + LocationID);
16475  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
16476 
16477  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
16478  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode; //added at v2.9.1 to give more info to user
16479 
16480  Prefix = " ERROR: ";
16481  if(ActionEventType == FailTrainEntry)
16482  {
16483  Prefix = " HELD: ";
16484  ErrorLog = " can't enter railway, train obstructing entry position ";
16485  WarningStr = " can't enter railway, train obstructing entry position ";
16486  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
16487  }
16488  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
16489  {
16490  Prefix = " HELD: ";
16491  ErrorLog = " can't enter railway, route set against it at entry position ";
16492  WarningStr = " can't enter railway, route set against it at entry position ";
16493  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
16494  }
16495  else if(ActionEventType == FailCreateTrain)
16496  {
16497  Prefix = " HELD: ";
16498  ErrorLog = " can't be created, train obstructing ";
16499  WarningStr = " can't be created, train obstructing ";
16500  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
16501  }
16502  else if(ActionEventType == FailCreateLockedRoute)
16503  {
16504  Prefix = " HELD: ";
16505  ErrorLog = " can't be created on a locked route at ";
16506  WarningStr = " can't be created on a locked route at ";
16507  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
16508  }
16509  else if(ActionEventType == FailEnterLockedRoute)
16510  {
16511  Prefix = " HELD: ";
16512  ErrorLog = " can't enter on a locked route at ";
16513  WarningStr = " can't enter on a locked route at ";
16514  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
16515  }
16516  else if(ActionEventType == FailCreatePoints)
16517  {
16518  Prefix = " HELD: ";
16519  ErrorLog = " can't be created, points set wrongly at ";
16520  WarningStr = " can't be created, points set wrongly at ";
16521  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
16522  }
16523  else if(ActionEventType == FailUnexpectedExitRailway)
16524  {
16525  ErrorLog = " left railway unexpectedly at ";
16526  UnexpectedExits++;
16527  }
16528  else if(ActionEventType == FailIncorrectExit)
16529  {
16530  ErrorLog = " left railway at an incorrect exit at ";
16531  IncorrectExits++;
16532  }
16533  else if(ActionEventType == FailLocTooShort)
16534  {
16535  ErrorLog = " failed to split - location too short at ";
16536  WarningStr = " failed to split, location too short at ";
16537  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
16538  }
16539  else if(ActionEventType == FailSplitDueToOtherTrain)
16540  {
16541  Prefix = " HELD: ";
16542  ErrorLog = " unable to split - other train obstructing at ";
16543  WarningStr = " unable to split - other train obstructing at ";
16544  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
16545  }
16546  else if(ActionEventType == FailUnexpectedBuffers)
16547  {
16548  ErrorLog = " stopped at buffers unexpectedly at position ";
16549  }
16550  else if(ActionEventType == FailMissedArrival)
16551  {
16552  ErrorLog = " failed to stop at ";
16553  MissedStops++;
16554  }
16555  else if(ActionEventType == FailMissedSplit)
16556  {
16557  ErrorLog = " failed to split at ";
16559  }
16560  else if(ActionEventType == FailMissedJBO)
16561  {
16562  ErrorLog = " failed to be joined by other train at ";
16564  }
16565  else if(ActionEventType == FailMissedDSC) //new at v2.15.0
16566  {
16567  ErrorLog = " failed to change its description at ";
16568 // OtherMissedEvents++; shouldn't count
16569  }
16570  else if(ActionEventType == FailMissedJoinOther)
16571  {
16572  ErrorLog = " failed to join other train at ";
16574  }
16575  else if(ActionEventType == FailMissedTerminate)
16576  {
16577  ErrorLog = " failed to terminate at ";
16579  }
16580  else if(ActionEventType == FailMissedNewService)
16581  {
16582  ErrorLog = " failed to form new service at ";
16584  }
16585  else if(ActionEventType == FailMissedExitRailway)
16586  {
16587  ErrorLog = " failed to exit railway ";
16589  }
16590  else if(ActionEventType == FailMissedChangeDirection)
16591  {
16592  ErrorLog = " failed to change direction at ";
16593 // OtherMissedEvents++; //dropped at v2.12.0 as cdt shouldn't count
16594  }
16595  else if(ActionEventType == FailMissedPass)
16596  {
16597  ErrorLog = " failed to pass ";
16598 // OtherMissedEvents++; //dropped at v2.12.0 as missed pass shouldn't count
16599  }
16600  else if(ActionEventType == FailBuffersPreventingStart)
16601  {
16602  ErrorLog = " facing buffers and unable to start at ";
16603  }
16604  else if(ActionEventType == FailDerailed)
16605  {
16606  ErrorLog = " DERAILED at position ";
16607  Prefix = " DERAILMENT: ";
16608  Derailments++;
16609  }
16610  else if(ActionEventType == FailBufferCrash)
16611  {
16612  ErrorLog = " CRASHED INTO BUFFERS at ";
16613  Prefix = " CRASH: ";
16614  CrashedTrains++;
16615  }
16616  else if(ActionEventType == FailLevelCrossingCrash)
16617  {
16618  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
16619  Prefix = " CRASH: ";
16620  CrashedTrains++;
16621  }
16622  else if(ActionEventType == FailCrashed)
16623  {
16624  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
16625  Prefix = " CRASH: ";
16626  CrashedTrains++;
16627  CrashedTrains++;
16628  }
16629  else if(ActionEventType == FailSPAD)
16630  {
16631  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
16632  Prefix = " SPAD: ";
16633  SPADEvents++;
16634  }
16635  else if(ActionEventType == FailLockedRoute)
16636  {
16637  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
16638  Prefix = " SPAD RISK: ";
16639  SPADRisks++;
16640  }
16641  else if(ActionEventType == RouteForceCancelled)
16642  {
16643  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
16644  }
16645  else if(ActionEventType == WaitingForJBO)
16646  {
16647  Prefix = " WARNING: ";
16648  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
16649  WarningStr = " waiting to join " + OtherHeadCode + " at ";
16650  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
16651  }
16652  else if(ActionEventType == WaitingForFJO)
16653  {
16654  Prefix = " WARNING: ";
16655  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
16656  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
16657  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
16658  }
16659 
16660  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
16661  PerfLogForm->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
16662  Utilities->CallLogPop(1371);
16663 }
16664 
16665 // ---------------------------------------------------------------------------
16666 
16668 {
16669 /* //for testing purposes
16670  TrainDataEntry
16671  AnsiString HeadCode, Description;//null on creation
16672  int StartSpeed, MaxRunningSpeed;//both kph
16673  int RepeatNumber;
16674  TActionVector ActionVector;
16675  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
16676  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
16677 
16678  ActionVectorEntry
16679  TTimetableEntryType FormatType;
16680  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
16681  AnsiString LocationName, Command, OtherHeadCode;//null on creation
16682  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
16683  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
16684  int RepeatNumber;
16685 
16686  TrainOperatingData
16687  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
16688  int TrainID;
16689  TRunningEntry RunningEntry;
16690  TDateTime StartTime;
16691  AnsiString HeadCode;
16692 */
16693  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
16694  std::ofstream OutFile("TrainData.csv");
16695 
16696  if(OutFile == 0)
16697  {
16698  ShowMessage("Output file TrainData.csv failed to open");
16699  Utilities->CallLogPop(1372);
16700  return;
16701  }
16702  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
16703  {
16704  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
16705  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
16706 
16707  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.FixedDescription.c_str() /* name changed at v2.16.1*/
16708  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
16709 
16710  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
16711  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
16712  "RepeatNumber" << '\n' << '\n';
16713  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
16714  {
16715  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
16716  AnsiString TimetableEntryTypeStr;
16717  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
16718  switch(AVEntry.FormatType)
16719  {
16720  case 0:
16721  {
16722  TimetableEntryTypeStr = "NoFormat";
16723  break;
16724  }
16725 
16726  case 1:
16727  {
16728  TimetableEntryTypeStr = "TimeLoc";
16729  break;
16730  }
16731 
16732  case 2:
16733  {
16734  TimetableEntryTypeStr = "TimeTimeLoc";
16735  break;
16736  }
16737 
16738  case 3:
16739  {
16740  TimetableEntryTypeStr = "TimeCmd";
16741  break;
16742  }
16743 
16744  case 4:
16745  {
16746  TimetableEntryTypeStr = "StartNew";
16747  break;
16748  }
16749 
16750  case 5:
16751  {
16752  TimetableEntryTypeStr = "TimeCmdHeadCode";
16753  break;
16754  }
16755 
16756  case 6:
16757  {
16758  TimetableEntryTypeStr = "FinRemHere";
16759  break;
16760  }
16761 
16762  case 7:
16763  {
16764  TimetableEntryTypeStr = "FNSShuttle";
16765  break;
16766  }
16767 
16768  case 8:
16769  {
16770  TimetableEntryTypeStr = "SNTShuttle";
16771  break;
16772  }
16773 
16774  case 9:
16775  {
16776  TimetableEntryTypeStr = "SNSShuttle";
16777  break;
16778  }
16779 
16780  case 10:
16781  {
16782  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
16783  break;
16784  }
16785 
16786  case 11:
16787  {
16788  TimetableEntryTypeStr = "FSHNewService";
16789  break;
16790  }
16791 
16792  case 12:
16793  {
16794  TimetableEntryTypeStr = "Repeat";
16795  break;
16796  }
16797 
16798  default:
16799  {
16800  TimetableEntryTypeStr = "Default";
16801  break;
16802  }
16803  }
16804  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
16805  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
16806  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
16807  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
16808  AVEntry.NumberOfRepeats << '\n';
16809  }
16810  OutFile << '\n';
16811  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
16812  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
16813  {
16814  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
16815  AnsiString RunningEntryStr;
16816  // NotStarted, Running, Exited
16817  switch(TOD.RunningEntry)
16818  {
16819  case 0:
16820  {
16821  RunningEntryStr = "NotStarted";
16822  break;
16823  }
16824 
16825  case 1:
16826  {
16827  RunningEntryStr = "Running";
16828  break;
16829  }
16830 
16831  case 2:
16832  {
16833  RunningEntryStr = "Exited";
16834  break;
16835  }
16836  }
16837  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
16838  }
16839  OutFile << '\n';
16840  }
16841  OutFile.close();
16842  Utilities->CallLogPop(1373);
16843 }
16844 
16845 // ---------------------------------------------------------------------------
16846 
16847 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
16848 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
16849 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed (in ClockTimer2 when StopTTClockFlag is false)
16850 {
16851  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
16852  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
16854  ShowMessage(Message);
16855  BaseTime = TDateTime::CurrentDateTime();
16856  StopTTClockFlag = false;
16857  Utilities->CallLogPop(1374);
16858 }
16859 
16860 // ---------------------------------------------------------------------------
16861 
16862 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
16863 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
16864 // from the start of the relevant vectors. Can't save the pointer values
16865 // as these will be different each time the vectors are created
16866 {
16867  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
16868  Utilities->SaveFileInt(SessionFile, TrainVector.size());
16869  for(unsigned int x = 0; x < TrainVector.size(); x++)
16870  {
16871  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
16872  }
16873  Utilities->CallLogPop(1375);
16874 }
16875 
16876 // ---------------------------------------------------------------------------
16877 
16878 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
16879 {
16880  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
16881  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
16882  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16883  // by zero error in calculating AValue, use 1
16884  for(int x = 0; x < NumberOfTrains; x++)
16885  {
16886  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
16887  // by zero error in calculating AValue, use 1
16888  NewTrain->LoadOneSessionTrain(0, SessionFile);
16889  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
16890  // added at v2.4.0. have to include as that value not stored in session file
16891  {
16892  NewTrain->StoppedWithoutPower = true;
16893  }
16894  TrainVector.push_back(*NewTrain);
16895  LastTrainLoaded = x;
16896  }
16897  delete NewTrain;
16898  Utilities->CallLogPop(1376);
16899 }
16900 
16901 // ---------------------------------------------------------------------------
16902 
16903 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
16904 {
16905  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
16906  int NumberOfTrains;
16907 
16908  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
16909  {
16910  Utilities->CallLogPop(1377);
16911  return(false);
16912  }
16913  for(int x = 0; x < NumberOfTrains; x++)
16914  {
16915  if(!(TTrain::CheckOneSessionTrain(InFile)))
16916  {
16917  Utilities->CallLogPop(1378);
16918  return(false);
16919  }
16920  }
16921  Utilities->CallLogPop(1379);
16922  return(true);
16923 }
16924 
16925 // ---------------------------------------------------------------------------
16926 
16927 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
16928 {
16929  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
16930  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
16931  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
16932  {
16933  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
16934  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RearTrackVectorPosition);
16935  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
16936  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
16937  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
16938  }
16939  Utilities->CallLogPop(1380);
16940 }
16941 
16942 // ---------------------------------------------------------------------------
16943 
16944 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
16945 {
16946  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
16947  TAllRoutes::TLockedRouteClass LockedRouteObject;
16948  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
16949 
16950  for(int x = 0; x < LockedRouteVectorSize; x++)
16951  {
16952  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
16953  LockedRouteObject.RearTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
16954  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
16955  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
16956  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
16957  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
16958  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
16959  }
16960  Utilities->CallLogPop(1381);
16961 }
16962 
16963 // ---------------------------------------------------------------------------
16964 
16965 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
16966 {
16967  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
16968  int LockedRouteVectorSize;
16969 
16970  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
16971  {
16972  Utilities->CallLogPop(1382);
16973  return(false);
16974  }
16975  for(int x = 0; x < LockedRouteVectorSize; x++)
16976  {
16977  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16978  {
16979  Utilities->CallLogPop(1383);
16980  return(false);
16981  }
16982  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16983  {
16984  Utilities->CallLogPop(1384);
16985  return(false);
16986  }
16987  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
16988  {
16989  Utilities->CallLogPop(1385);
16990  return(false);
16991  }
16992  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
16993  {
16994  Utilities->CallLogPop(1386);
16995  return(false);
16996  }
16997  if(!Utilities->CheckFileDouble(SessionFile))
16998  {
16999  Utilities->CallLogPop(1387);
17000  return(false);
17001  }
17002  }
17003  Utilities->CallLogPop(1388);
17004  return(true);
17005 }
17006 
17007 // ---------------------------------------------------------------------------
17008 
17009 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
17010 {
17011  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
17012  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
17013  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
17014  {
17015  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
17016  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
17017  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
17018  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
17019  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
17020  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
17021  }
17022  Utilities->CallLogPop(1389);
17023 }
17024 
17025 // ---------------------------------------------------------------------------
17026 
17027 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
17028 {
17029  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
17030  TContinuationAutoSigEntry ContinuationAutoSigObject;
17031  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
17032 
17033  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
17034  {
17035  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
17036  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
17037  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
17038  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
17039  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
17040  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
17041  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
17042  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
17043  }
17044  Utilities->CallLogPop(1390);
17045 }
17046 
17047 // ---------------------------------------------------------------------------
17048 
17049 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
17050 {
17051  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
17052  int ContinuationAutoSigVectorSize;
17053 
17054  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
17055  {
17056  Utilities->CallLogPop(1391);
17057  return(false);
17058  }
17059  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
17060  {
17061  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
17062  {
17063  Utilities->CallLogPop(1392);
17064  return(false);
17065  }
17066  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
17067  {
17068  Utilities->CallLogPop(1393);
17069  return(false);
17070  }
17071  if(!Utilities->CheckFileDouble(SessionFile))
17072  {
17073  Utilities->CallLogPop(1405);
17074  return(false);
17075  }
17076  if(!Utilities->CheckFileDouble(SessionFile))
17077  {
17078  Utilities->CallLogPop(1406);
17079  return(false);
17080  }
17081  if(!Utilities->CheckFileDouble(SessionFile))
17082  {
17083  Utilities->CallLogPop(1407);
17084  return(false);
17085  }
17086  if(!Utilities->CheckFileDouble(SessionFile))
17087  {
17088  Utilities->CallLogPop(1394);
17089  return(false);
17090  }
17091  }
17092  Utilities->CallLogPop(1395);
17093  return(true);
17094 }
17095 
17096 // ---------------------------------------------------------------------------
17097 
17098 /*
17099  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
17100  {
17101  public:
17102  AnsiString Description; ///< service description
17103  AnsiString HeadCode; ///< service headcode
17104  int RepeatNumber; ///< service RepeatNumber
17105  int IncrementalMinutes; ///< Repeat separation in minutes
17106  int IncrementalDigits; ///< Repeat headcode separation
17107  int VectorPosition; ///< TrackVectorPosition for the continuation element
17108  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
17109  };
17110 
17111 
17112  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
17113  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
17114 */
17115 
17117 // build this into timetable load so session loading can use it too
17118 // being a multimap it automatically sorts in ascending EventTime order
17119 {
17120  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
17122  // need to clear as this called twice when load a session
17123  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17124  {
17125  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
17126  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
17127  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
17128 
17129  if(AVFirstEntry.Command == "Snt")
17130  // new train (no need to include Snt-sh since they can't start at a continuation)
17131  {
17134  {
17136  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
17137  // retains this value for all repeats
17138  CTEEntry.RepeatNumber = 0; // for first entry
17139  CTEEntry.TrainDataEntryPtr = &TDEntry;
17140  // retains this value for all repeats
17141  CTEEntry.HeadCode = TDEntry.HeadCode;
17142  CTEEntry.FixedDescription = TDEntry.FixedDescription; //name changed at v2.16.1
17143  CTEEntry.IncrementalMinutes = 0;
17144  CTEEntry.IncrementalDigits = 0;
17145  if(AVLastEntry.FormatType == Repeat)
17146  {
17147  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
17148  // retains this value or 0 for all repeats
17149  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
17150  // retains this value or 0 for all repeats
17151  }
17152  CTEMMP.first = AVFirstEntry.EventTime;
17153  CTEMMP.second = CTEEntry;
17154  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
17155  // base entry
17156  if(TDEntry.NumberOfTrains > 1)
17157  {
17158  if(AVLastEntry.FormatType != Repeat)
17159  {
17160  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
17161  }
17162  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
17163  {
17164  CTEEntry.RepeatNumber = y;
17165  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
17166  // CTEEntry.VectorPosition stays same
17167  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
17168  CTEMMP.second = CTEEntry;
17169  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
17170  }
17171  }
17172  }
17173  }
17174  }
17175  Utilities->CallLogPop(1396);
17176 }
17177 
17178 // ---------------------------------------------------------------------------
17179 
17181 {
17182  // called when WarningFlashCount == 0 or when press zoomout button
17183  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
17184  if(!Display->ZoomOutFlag)
17185  {
17186  Utilities->CallLogPop(1156);
17187  return;
17188  }
17189  for(unsigned int x = 0; x < TrainVector.size(); x++)
17190  {
17191  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
17192  // if OldPlotElement[x] == -1 then ignore (not plotted)
17194  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
17195  }
17196  Display->Update();
17197  // need to keep this since Update() not called for PlotSmallOutput as too slow
17198  Utilities->CallLogPop(742);
17199 }
17200 
17201 // ---------------------------------------------------------------------------
17202 
17203 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
17204 {
17205  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
17206  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
17207  {
17208  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
17209  }
17210  Utilities->CallLogPop(740);
17211  return(TrainVector.at(VecPos));
17212 }
17213 
17214 // ---------------------------------------------------------------------------
17215 
17216 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
17217 {
17218  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
17219  AnsiString RetStr = "", PartStr = "";
17220 
17221 
17222 /*
17223  Have description & mass etc for train at top - header, then array of actions
17224 
17225  class TActionVectorEntry
17226  {
17227  public:
17228  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
17230  bool SignallerControl;
17232  bool Warning;
17234  int NumberOfRepeats;
17236  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
17238  TDateTime EventTime, ArrivalTime, DepartureTime;
17240  TNumList ExitList;
17242  TTimetableFormatType FormatType;
17244  TTimetableLocationType LocationType;
17246  TTimetableSequenceType SequenceType;
17248  TTimetableShuttleLinkType ShuttleLinkType;
17250  TTrainDataEntry *LinkedTrainEntryPtr;
17252  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
17254 
17255  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
17256 
17257  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
17258 
17259  class TTrainOperatingData
17260  {
17261  public:
17262  int TrainID;
17263  TActionEventType EventReported;
17264  TRunningEntry RunningEntry;
17265 
17266  //inline function
17267  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
17268  };
17269 
17270  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
17271 
17272  class TTrainDataEntry
17273  {
17274  public:
17275  AnsiString HeadCode, ServiceReference, Description;
17277  double MaxBrakeRate;
17279  double MaxRunningSpeed;
17281  double PowerAtRail;
17283  int Mass;
17285  int NumberOfTrains;
17287  int SignallerSpeed;
17289  int StartSpeed;
17291  TActionVector ActionVector;
17293  TTrainOperatingDataVector TrainOperatingDataVector;
17295 
17296  //inline function
17297  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
17298  };
17299 
17300  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
17301 
17302  //formatted timetable types
17303  class TOneTrainFormattedEntry
17304  {
17305  AnsiString Action;//includes location if relevanr
17306  AnsiString Time;
17307  };
17308 
17309  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
17310 
17311  class TOneCompleteFormattedTrain//headcode + list of actions
17312  {
17313  public:
17314  AnsiString HeadCode;
17315  TOneFormattedTrainVector OneFormattedTrainVector;
17316  };
17317 
17318  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
17319 
17320  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
17321  {
17322  public:
17323  AnsiString Header;//description, mass, power, brake rate etc
17324  int NumberOfTrains;// number of repeats + 1
17325  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
17326  };
17327 
17328 
17329  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
17330  //end of formatted timetable types
17331 
17332 */
17333 
17334  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17335 
17336  // format "16/06/2009 20:55:17"
17337  // avoid characters in filename:= / \ : * ? " < > |
17338  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
17339 
17340  AnsiString ShortTTName = "";
17341 
17342  for(int x = TTFileName.Length(); x > 0; x--)
17343  {
17344  if(TTFileName[x] == '\\')
17345  {
17346  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
17347  break;
17348  }
17349  }
17350 
17351  ShowMessage("Creates two timetables named " + ShortTTName +
17352  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
17353 
17354  Screen->Cursor = TCursor(-11); // Hourglass
17355 
17356  AnsiString FormatNoDPStr = "#######0";
17357  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
17358 
17360  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
17361  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
17362 
17363  // all timetable in formatted form
17364  //create the AllTTTrains vector
17365  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
17366  {
17367  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
17368  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
17369  if(TrainDataEntry.Mass > 0)
17370  {
17371  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
17372  }
17373  if(TrainDataEntry.PowerAtRail > 0)
17374  {
17375  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
17376  }
17377  if(TrainDataEntry.MaxBrakeRate > 0)
17378  {
17379  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
17380  }
17381  if(TrainDataEntry.MaxRunningSpeed > 0)
17382  {
17383  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
17384  }
17385  FirstHeadCode = TrainDataEntry.HeadCode;
17386  int IncDigits = 0, IncMinutes = 0;
17387  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17388  if(!ActionVector.empty())
17389  {
17390  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
17391  {
17392  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
17393  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
17394  }
17395  }
17396  TTrainFormattedInformation OneTTLine;
17397  // contains all information for a single TT entry (including repeats)
17398  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
17399  {
17400  OneTTLine.Header = "";
17401  if((TrainDataEntry.FixedDescription != "") && (MassStr != "")) //name changed at v2.16.1
17402  {
17403  OneTTLine.Header = TrainDataEntry.FixedDescription + MassStr + PowerStr + BrakeStr + MaxSpeedStr; //name changed at v2.16.1
17404  }
17405  else if(TrainDataEntry.FixedDescription != "") //name changed at v2.16.1
17406  {
17407  OneTTLine.Header = TrainDataEntry.FixedDescription; //name changed at v2.16.1
17408  }
17409  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
17410  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
17411  for(unsigned int z = 0; z < ActionVector.size(); z++)
17412  {
17413  TOneTrainFormattedEntry OneTTEntry;
17414  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
17415  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
17416  AnsiString PartStr = "", TimeStr = "";
17417 /*
17418  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
17419  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
17420  ExitRailway};
17421  enum TTimetableSequenceType {NoSequence, StartSequence, FinishSequence, IntermediateSequence, SequTypeForRepeatEntry};
17422  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
17423  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
17424 */
17425  if(ActionVectorEntry.SequenceType == StartSequence)
17426  {
17427  if(ActionVectorEntry.FormatType == StartNew)
17428  {
17429  if(ActionVectorEntry.LocationName != "")
17430  {
17431  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
17432  {
17433  PartStr = "Enters at " + ActionVectorEntry.LocationName;
17434  }
17435  else
17436  {
17437  PartStr = "Created at " + ActionVectorEntry.LocationName;
17438  }
17439  }
17440  else // may be a named continuation or other element, and if so report that
17441  {
17442  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
17443  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
17444  {
17445  if(LocName != "")
17446  {
17447  PartStr = "Enters at " + LocName;
17448  }
17449  else // use rear position if it's a continuation
17450  {
17451  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
17452  }
17453  }
17454  else // not a continuation
17455  {
17456  if(LocName != "")
17457  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
17458  // but include anyway
17459  {
17460  PartStr = "Created at " + LocName;
17461  }
17462  else // use rear position again
17463  {
17464  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
17465  }
17466  }
17467  }
17468  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
17469  }
17470  else if(ActionVectorEntry.FormatType == SNTShuttle)
17471  {
17472  if(y == 0) // first train
17473  {
17474  PartStr = "Enters at " + ActionVectorEntry.LocationName;
17475  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
17476  }
17477  else
17478  {
17479  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
17480  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
17481  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
17482  } // y-1 for headcode above since it is the last repeat value that the train is from
17483 
17484  }
17485  else if(ActionVectorEntry.Command == "Sfs")
17486  {
17487  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
17488  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17489  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
17490  }
17491  else if(ActionVectorEntry.Command == "Sns")
17492  {
17493  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17494  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17495  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
17496  }
17497  else if(ActionVectorEntry.FormatType == SNSShuttle)
17498  {
17499  if(y == 0) // first entry from shuttle
17500  {
17501  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17502  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
17503  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
17504  }
17505  else
17506  {
17507  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
17508  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
17509  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
17510  } // y-1 for headcode above since it is the last repeat value that the train is from
17511 
17512  }
17513  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
17514  {
17515  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
17516  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
17517  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
17518  AnsiString FirstHeadCode = TDE->HeadCode;
17519  int LastRepeatNumber = TDE->NumberOfTrains - 1;
17520  // a shuttle has to have at least 1 repeat
17521  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
17522  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
17523  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
17524  }
17525  }
17526  else if(ActionVectorEntry.SequenceType == IntermediateSequence)
17527  {
17528  if(ActionVectorEntry.FormatType == TimeTimeLoc)
17529  {
17530  // here need 2 entries if times different so push the first right away & the second later
17531  // if times same just give the arrival entry
17532  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
17533  {
17534  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
17535  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17536  OneTTEntry.Action = PartStr;
17537  OneTTEntry.Time = TimeStr;
17538  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
17539  PartStr = "Departs from " + ActionVectorEntry.LocationName;
17540  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
17541  }
17542  else
17543  {
17544  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
17545  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17546  }
17547  }
17548  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
17549  {
17550  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
17551  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
17552  }
17553  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
17554  {
17555  PartStr = "Departs from " + ActionVectorEntry.LocationName;
17556  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
17557  }
17558  else if(ActionVectorEntry.FormatType == PassTime)
17559  {
17560  PartStr = "Passes " + ActionVectorEntry.LocationName;
17561  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
17562  }
17563  else if(ActionVectorEntry.Command == "jbo")
17564  {
17565  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
17566  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17567  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
17568  }
17569  else if(ActionVectorEntry.Command == "fsp")
17570  {
17571  if(ActionVectorEntry.SplitDistribution != "") //new at v2.15.0
17572  {
17573  PartStr = "Splits from front [mass%-power% = " + ActionVectorEntry.SplitDistribution + "] at " + ActionVectorEntry.LocationName + " to form";
17574  }
17575  else
17576  {
17577  PartStr = "Splits from front [mass%-power% = 50-50] at " + ActionVectorEntry.LocationName + " to form";
17578  }
17579  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17580  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
17581  }
17582  else if(ActionVectorEntry.Command == "rsp")
17583  {
17584  if(ActionVectorEntry.SplitDistribution != "") //new at v2.15.0
17585  {
17586  PartStr = "Splits from front [mass%-power% = " + ActionVectorEntry.SplitDistribution + "] at " + ActionVectorEntry.LocationName + " to form";
17587  }
17588  else
17589  {
17590  PartStr = "Splits from front [mass%-power% = 50-50] at " + ActionVectorEntry.LocationName + " to form";
17591  }
17592  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17593  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
17594  }
17595  else if(ActionVectorEntry.Command == "cdt")
17596  {
17597  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
17598  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
17599  }
17600  else if(ActionVectorEntry.Command == "dsc")
17601  {
17602  PartStr = "Changes description at " + ActionVectorEntry.LocationName;
17603  TimeStr = Utilities->Format96HHMM(GetRepeatTime(76, ActionVectorEntry.EventTime, y, IncMinutes));
17604  }
17605  }
17606  else if(ActionVectorEntry.SequenceType == FinishSequence)
17607  {
17608  if(ActionVectorEntry.Command == "Fns")
17609  {
17610  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17611  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17612  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
17613  }
17614  else if(ActionVectorEntry.Command == "F-nshs")
17615  {
17616  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17617  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
17618  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
17619  }
17620  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
17621  {
17622  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
17623  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
17624  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
17625  // y+1 because it's the NEXT service repeat number that is relevant
17626  }
17627  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
17628  {
17629  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17630  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
17631  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
17632  }
17633  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
17634  {
17635  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
17636  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
17637  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
17638  // y+1 because it's the NEXT service repeat number that is relevant
17639  }
17640  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
17641  {
17642  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
17643  // only used in chronological tt
17644  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
17645  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
17646  }
17647  else if(ActionVectorEntry.Command == "Frh")
17648  {
17649  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
17650  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
17651  if(z > 0)
17652  // should be for finish entry but include check for safety
17653  {
17654  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
17655  {
17656  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
17657  }
17658  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
17659  {
17660  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
17661  }
17662  else
17663  {
17664  TimeStr = " "; // shouldn't ever get here
17665  }
17666  }
17667  }
17668  else if(ActionVectorEntry.Command == "Fer")
17669  {
17670  AnsiString AllowedExits;
17671  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
17672  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
17673  }
17674  else if(ActionVectorEntry.Command == "Fjo")
17675  {
17676  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
17677  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
17678  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
17679  }
17680  }
17681  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
17682  {
17683  continue; // no entry needed for a repeat
17684  }
17685  OneTTEntry.Action = PartStr;
17686  OneTTEntry.Time = TimeStr;
17687  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
17688  // one per action
17689  }
17690  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
17691  // one per repeat
17692  }
17693  AllTTTrains->push_back(OneTTLine); // one per repeating train
17694  }
17695  // AllTTTrains vector now complete
17696 
17697  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
17698 
17699  if(TTFile == 0)
17700  {
17701  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
17702  delete AllTTTrains;
17703  Utilities->CallLogPop(1567);
17704  return;
17705  }
17706 /* formatted timetable types
17707  class TOneTrainFormattedEntry
17708  {
17709  AnsiString Action;//includes location if relevant
17710  AnsiString Time;
17711  };
17712 
17713  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
17714 
17715  class TOneCompleteFormattedTrain//headcode + list of actions
17716  {
17717  public:
17718  AnsiString HeadCode;
17719  TOneFormattedTrainVector OneFormattedTrainVector;
17720  };
17721 
17722  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
17723 
17724  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
17725  {
17726  public:
17727  AnsiString Header;//description, mass, power, brake rate etc
17728  int NumberOfTrains;// number of repeats + 1
17729  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
17730  };
17731 
17732  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
17733  //end of formatted timetable types
17734 */
17735 
17736  // new layout using multiple rows
17737  TTFile << TableTitle.c_str() << '\n' << '\n';
17738  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17739  {
17740  TTFile << AllTTTrains->at(x).Header.c_str();
17741  TTFile << '\n';
17742  TTFile << ','; // for the blank line above the action list
17743  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17744  {
17745  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
17746  {
17747  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
17748  }
17749  else
17750  {
17751  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
17752  }
17753  }
17754  TTFile << '\n' << '\n';
17755 
17756  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
17757  {
17758  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
17759  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17760  {
17761  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
17762  {
17763  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
17764  }
17765  else
17766  {
17767  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
17768  }
17769  }
17770  TTFile << '\n';
17771  }
17772  TTFile << '\n' << '\n';
17773  }
17774 
17775  TTFile.close();
17776 
17777  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17778 
17779  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
17780 
17781  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
17782 
17783  if(TTFile2 == 0)
17784  {
17785  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
17786  delete AllTTTrains;
17787  Utilities->CallLogPop(1710);
17788  return;
17789  }
17790  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
17791  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
17792  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
17793 
17794  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
17795  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
17796 
17797  // multimap of AnsiStrings with TimeString as key (to sort automatically)
17798 
17799  TTFile2 << TableTitle.c_str() << '\n' << '\n';
17800  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
17801  {
17802  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
17803  {
17804  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
17805  {
17806  bool GiveMessagesFalse = false;
17807  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
17808  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
17809  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
17810  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
17811  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
17812  {
17813  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
17814  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
17815  TimeString = TimeString.SubString(9, 5);
17816  ActionString += " " + OtherHeadCode;
17817  }
17818  if(TimeString.SubString(1, 7) == "End at ")
17819  // for Frh-sh final entry
17820  {
17821  TimeString = TimeString.SubString(8, 5);
17822  }
17823  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
17824  AnsiMultiMapEntry.first = TimeString;
17825  AnsiMultiMapEntry.second = OneLine;
17826  TAMM->insert(AnsiMultiMapEntry);
17827  }
17828  }
17829  }
17830 
17831  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
17832  {
17833  TTFile2 << (AMMIT->second).c_str();
17834  }
17835  delete AllTTTrains;
17836  delete TAMM;
17837  TTFile2.close();
17838  Utilities->CallLogPop(1580);
17839 }
17840 
17841 // ---------------------------------------------------------------------------
17842 
17843 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
17844  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
17845 {
17846 
17847  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
17848  bool AnalysisError = false;
17849  AnsiString SequenceLog = "SequenceLog\n";
17850 
17851 /* Double crosslink (shuttle) table:
17852 
17853 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
17854  Code ShuttleLink- EntryPtr ShuttleLink-
17855  HeadCode EntryPtr
17856 
17857 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
17858 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
17859 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
17860 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
17861 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
17862 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
17863 
17864 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
17865 */
17866 
17867  try
17868  {
17869  //New section at v2.5.0 for tt conflict analysis
17870  /*
17871  typedef std::list<AnsiString> TServiceCallingLocsList;
17872  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
17873 
17875  struct TLocServiceTimes
17876  {
17877  AnsiString Location;
17878  AnsiString ServiceAndRepeatNum;
17879  AnsiString AtLocTime;
17880  AnsiString ArrTime;
17881  AnsiString DepTime;
17882  AnsiString FrhMarker;
17883  };
17884  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
17885  */
17886 
17887  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
17888  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
17889 
17890 //create TrainDataVectorCopy and populate service refs with /1, /2 etc
17891  TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
17892  TTrainDataVector::iterator TDVIt, TDVCopyIt;
17893  int Suffix = 0;
17894  int IteratorNumber = 0;
17895  AnsiString AnsiSuffix = "";
17896  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
17897  {
17898  IteratorNumber++; //first value in loop is 1
17899  Suffix = 0;
17900  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
17901  {
17902  if(TDVCopyIt->HeadCode == TDVIt->HeadCode)
17903  {
17904  Suffix++; //first value is 1
17905  AnsiSuffix = AnsiString(Suffix);
17906  TDVCopyIt->ServiceReference = TDVIt->HeadCode + "/" + AnsiSuffix; //set both the HeadCode + any forward slashes and numbers, this is because sometimes
17907  TDVCopyIt->HeadCode = TDVIt->HeadCode + "/" + AnsiSuffix; //service refs are used and sometimes H/Cs, so need them to be the same,
17908  } //they are all unique at this point anyway
17909  }
17910  }
17911 //now make all linked pointers in ActionVectorEntries point to links in the vector copy (still point to original vector at this stage)
17912 //and set the linked headcodes to the correct values - all unique at this point
17913  int Increment = 0, SlashPos;
17914  TActionVectorIterator AVEIt;
17915  AnsiString LinkedHeadCode;
17916 
17917  for(TDVCopyIt = TrainDataVectorCopy.begin(); TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
17918  {
17919  for(AVEIt = TDVCopyIt->ActionVector.begin(); AVEIt != TDVCopyIt->ActionVector.end(); AVEIt++)
17920  {
17921  if(AVEIt->LinkedTrainEntryPtr != NULL)
17922  {
17923  Increment = AVEIt->LinkedTrainEntryPtr - &TrainDataVector.at(0);
17924  AVEIt->LinkedTrainEntryPtr = &TrainDataVectorCopy.at(0) + Increment;
17925  //now set AVEIt->OtherHeadCode to the linked headcodes with /1, /2 etc
17926  //but note that these ARE HeadCodes and not service refs, so need to strip off any prefixes from the linked service refs
17927  LinkedHeadCode = (*AVEIt->LinkedTrainEntryPtr).ServiceReference;
17928  //now count from back until reach a '/' character or a non-integer character, if reach non-integer first then no '/' present at end of H/c (but may be one earlier as a prefix)
17929  SlashPos = 0;
17930  for(int x = LinkedHeadCode.Length(); x > 0; x--)
17931  {
17932  if(LinkedHeadCode[x] == '/')
17933  {
17934  SlashPos = LinkedHeadCode.Length() - x + 1;
17935  break;
17936  }
17937  else if((LinkedHeadCode[x] != '0') && (LinkedHeadCode[x] != '1') && (LinkedHeadCode[x] != '2') && (LinkedHeadCode[x] != '3') &&
17938  (LinkedHeadCode[x] != '4') && (LinkedHeadCode[x] != '5') && (LinkedHeadCode[x] != '6') && (LinkedHeadCode[x] != '7') &&
17939  (LinkedHeadCode[x] != '8') && (LinkedHeadCode[x] != '8'))
17940  {
17941  break;
17942  }
17943  }
17944  //now strip off any prefix
17945  AVEIt->OtherHeadCode = LinkedHeadCode.SubString(LinkedHeadCode.Length() - 3 - SlashPos, 4 + SlashPos);
17946  }
17947  else
17948  {
17949  AVEIt->OtherHeadCode = "";
17950  }
17951  if(AVEIt->NonRepeatingShuttleLinkEntryPtr != NULL)
17952  {
17953  Increment = AVEIt->NonRepeatingShuttleLinkEntryPtr - &TrainDataVector.at(0);
17954  AVEIt->NonRepeatingShuttleLinkEntryPtr = &TrainDataVectorCopy.at(0) + Increment;
17955  //now set AVEIt->NonRepeatingShuttleLinkHeadCode to the linked headcodes with /1, /2 etc
17956  //but note that these ARE HeadCodes and not service refs, so need to strip off any prefixes from the linked service refs
17957  LinkedHeadCode = (*AVEIt->NonRepeatingShuttleLinkEntryPtr).ServiceReference;
17958  //now count from back until reach a '/' character or a non-integer character, if reach non-integer first then no '/' present at end of H/c (but may be one earlier as a prefix)
17959  SlashPos = 0;
17960  for(int x = LinkedHeadCode.Length(); x > 0; x--)
17961  {
17962  if(LinkedHeadCode[x] == '/')
17963  {
17964  SlashPos = LinkedHeadCode.Length() - x + 1;
17965  break;
17966  }
17967  else if((LinkedHeadCode[x] != '0') && (LinkedHeadCode[x] != '1') && (LinkedHeadCode[x] != '2') && (LinkedHeadCode[x] != '3') &&
17968  (LinkedHeadCode[x] != '4') && (LinkedHeadCode[x] != '5') && (LinkedHeadCode[x] != '6') && (LinkedHeadCode[x] != '7') &&
17969  (LinkedHeadCode[x] != '8') && (LinkedHeadCode[x] != '8'))
17970  {
17971  break;
17972  }
17973  }
17974  //now strip off any prefix
17975  AVEIt->NonRepeatingShuttleLinkHeadCode = LinkedHeadCode.SubString(LinkedHeadCode.Length() - 3 - SlashPos, 4 + SlashPos);
17976  }
17977  else
17978  {
17979  AVEIt->NonRepeatingShuttleLinkHeadCode = "";
17980  }
17981  }
17982  }
17983  //from here only TrainDataVectorCopy used
17984  SequenceLog += "1\n";
17985  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
17986  TServiceCallingLocsList ServiceCallingLocsList;
17987  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
17988  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17989  {
17990  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
17991  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
17992  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
17993  ServiceCallingLocsList.clear();
17994  if(ActionVector.empty())
17995  {
17996  continue;
17997  }
17998  if(ActionVector.at(0).SignallerControl)
17999  {
18000  continue;
18001  }
18002  for(unsigned int z = 0; z < ActionVector.size(); z++)
18003  {
18004  TActionVectorEntry AVE = ActionVector.at(z);
18005  if(AVE.FormatType == StartNew)
18006  {
18007  if(AVE.LocationType == AtLocation) //located Snt
18008  {
18009  ServiceCallingLocsList.push_back(AVE.LocationName);
18010  }
18011  else //unlocated Snt (could be entering at continuation)
18012  {
18014  if(TE.ActiveTrackElementName != "")
18015  {
18016  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
18017  }
18018  else
18019  {
18020  int HLoc = TE.HLoc;
18021  int VLoc = TE.VLoc;
18022  AnsiString HString;
18023  AnsiString VString;
18024  if(HLoc < 0)
18025  {
18026  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18027  }
18028  else
18029  {
18030  HString = AnsiString(HLoc);
18031  }
18032  if(VLoc < 0)
18033  {
18034  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18035  }
18036  else
18037  {
18038  VString = AnsiString(VLoc);
18039  }
18040  ServiceCallingLocsList.push_back(HString + '-' + VString);
18041  }
18042  }
18043  }
18044  else if(AVE.SequenceType == StartSequence) //other start entries, all located
18045  {
18046  ServiceCallingLocsList.push_back(AVE.LocationName);
18047  }
18048  else if(AVE.FormatType == TimeLoc) //z must be > 0
18049  {
18050  if(ServiceCallingLocsList.back() != AVE.LocationName)
18051  {
18052  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
18053  }
18054  }
18055  else if(AVE.FormatType == PassTime)
18056  {
18057  ServiceCallingLocsList.push_back(AVE.LocationName);
18058  }
18059  else if(AVE.FormatType == TimeTimeLoc)
18060  {
18061  ServiceCallingLocsList.push_back(AVE.LocationName);
18062  }
18063  else if(AVE.Command == "cdt") //list if not next to start or finish
18064  {
18065  if(ActionVector.at(z-1).SequenceType == StartSequence)
18066  {
18067  continue;
18068  }
18069  else if(ActionVector.at(z+1).SequenceType == FinishSequence) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
18070  {
18071  continue;
18072  }
18073  else
18074  {
18075  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
18076  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
18077  }
18078  }
18079  else if(AVE.FormatType == ExitRailway) //Fer
18080  {
18081  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
18082  AnsiString LName = TE.ActiveTrackElementName;
18083  if(LName != "")
18084  {
18085  ServiceCallingLocsList.push_back(LName);
18086  }
18087  else
18088  {
18089  int HLoc = TE.HLoc;
18090  int VLoc = TE.VLoc;
18091  AnsiString HString;
18092  AnsiString VString;
18093  if(HLoc < 0)
18094  {
18095  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18096  }
18097  else
18098  {
18099  HString = AnsiString(HLoc);
18100  }
18101  if(VLoc < 0)
18102  {
18103  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18104  }
18105  else
18106  {
18107  VString = AnsiString(VLoc);
18108  }
18109  ServiceCallingLocsList.push_back(HString + '-' + VString);
18110  }
18111  }
18112  }
18113  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
18114  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
18115  }
18116  //AllServiceCallingLocsMap built
18117  SequenceLog += "2\n";
18118 /*
18119 // this sequence is to test the validity of AllServiceCallingLocsMap
18120  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
18121  std::ofstream Test(TestFile.c_str());
18122 
18123  if(TestFile == 0)
18124  {
18125  ShowMessage("TestFile failed to open - can't be created");
18126  Utilities->CallLogPop();
18127  return false;
18128  }
18129 
18130  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
18131  {
18132  Test << ASCLIt->first << '\n'; //service ref
18133  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
18134  {
18135  Test << *SCLIt << '\n';
18136  }
18137  Test << "\n\n";
18138  }
18139  Test.close();
18140  Utilities->CallLogPop();
18141  return true;
18142 */
18143  //initialise variables before calc LastTTTime & build LocServiceTimesVector
18144  if(TrainDataVectorCopy.empty())
18145  {
18146  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
18147  Utilities->CallLogPop(2209);
18148  return(false);
18149  }
18150  TLocServiceTimes TLSTEntry;
18151  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
18152  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
18153  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
18154  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
18155  LastTTTime = "";
18156  SequenceLog += "3\n";
18157  //calculate LastTTTime
18158  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18159  {
18160  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18161  TActionVector &ActionVector = TrainDataEntry.ActionVector;
18162  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
18163  TDateTime LastTDTime;
18164  int IncMinutes = 0;
18165  NumTrains = TrainDataEntry.NumberOfTrains;
18166  if(ActionVector.empty())
18167  {
18168  continue;
18169  }
18170  if(ActionVector.at(0).SignallerControl)
18171  {
18172  continue;
18173  }
18174  if(AVLast->FormatType == Repeat)
18175  {
18176  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
18177  AVLast--; //now points to the command before the repeat
18178  }
18179  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
18180  {
18181  AVLast--; //points to last timed entry
18182  }
18183  //here AVLast points to last entry with a time
18184  if(AVLast->ArrivalTime != TDateTime(-1))
18185  {
18186  LastTDTime = AVLast->ArrivalTime;
18187  }
18188  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
18189  {
18190  LastTDTime = AVLast->EventTime;
18191  }
18192  else
18193  {
18194  continue; //shouldn't ever reach here but if do then skip this service
18195  }
18196  if(NumTrains == 1)
18197  {
18198  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
18199  }
18200  else
18201  {
18202  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
18203  }
18204  if(LastAnsiTime > LastTTTime)
18205  {
18206  LastTTTime = LastAnsiTime;
18207  }
18208  }
18209  SequenceLog += "4\n";
18210 //build LocServiceTimesVector
18211 
18212 /*
18213  struct TLocServiceTimes
18214  {
18215  AnsiString Location;
18216  AnsiString ServiceAndRepeatNum;
18217  AnsiString AtLocTime;
18218  AnsiString ArrTime;
18219  AnsiString DepTime;
18220  AnsiString FrhMarker;
18221  };
18222  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
18223 
18224 This works as follows:
18225 ServiceAndRepeatNum is taken from the TrainDataVectorCopy as it is the same for all actionvector entries
18226 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
18227 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
18228 
18229 Every action for every train is examined and times entered as follows:-
18230 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
18231 b) an unlocated Snt: entry time becomes DepTime
18232 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
18233 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
18234 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
18235 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
18236 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
18237 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
18238 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
18239 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
18240 */
18241  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
18242  {
18243  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
18244  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
18245  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
18246  int IncMinutes = 0;
18247  NumTrains = TrainDataEntry.NumberOfTrains;
18248  if(ActionVector.empty())
18249  {
18250  continue;
18251  }
18252  if(ActionVector.at(0).SignallerControl)
18253  {
18254  continue;
18255  }
18256  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
18257  {
18258  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
18259  }
18260  for(int y = 0; y < NumTrains; y++) //y is the repeat number
18261  {
18262  if(NumTrains == 1)
18263  {
18264  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
18265  }
18266  else if(y == 0)
18267  {
18268  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
18269  }
18270  else
18271  {
18272  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
18273  }
18274  for(unsigned int z = 0; z < ActionVector.size(); z++)
18275  {
18276  TActionVectorEntry AVE = ActionVector.at(z);
18277  TLSTEntry.AtLocTime = "";
18278  TLSTEntry.ArrTime = "";
18279  TLSTEntry.DepTime = "";
18280  TLSTEntry.Location = "";
18281  TLSTEntry.FrhMarker = "";
18282 
18283  if(AVE.FormatType == StartNew) //Snt only
18284  {
18285  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
18286  {
18287  TLSTEntry.Location = AVE.LocationName;
18288  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
18289  LocServiceTimesVector.push_back(TLSTEntry);
18290 
18291  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18292  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
18293  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18294  {
18295  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18296  {
18297  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
18298  break;
18299  }
18300  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered in a later test
18301  {
18302  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
18303  break;
18304  }
18305  }
18306  if(FoundStopTime == "")
18307  {
18308  throw Exception("Failure to determine FoundStopTime for located Snt");
18309  }
18310  int WhileCount = 0;
18311  while(true)
18312  {
18313  //add minutes until reach FoundStopTime but don't add that time
18314  WhileCount++;
18315  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18316  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18317  TLSTEntry.DepTime = "";
18318  TLSTEntry.ArrTime = "";
18319  if(IncTime >= FoundStopTime) //don't add that time
18320  {
18321  break;
18322  }
18323  LocServiceTimesVector.push_back(TLSTEntry);
18324  if(WhileCount > 2000)
18325  {
18326  throw Exception("While loop failed to break in 2000 loops for located Snt");
18327  }
18328  }
18329  }
18330  else //unlocated Snt, use the EventTime as DepTime for this vector
18331  {
18333  if(TE.ActiveTrackElementName != "")
18334  {
18335  TLSTEntry.Location = TE.ActiveTrackElementName;
18336  }
18337  else
18338  {
18339  int HLoc = TE.HLoc;
18340  int VLoc = TE.VLoc;
18341  AnsiString HString;
18342  AnsiString VString;
18343  if(HLoc < 0)
18344  {
18345  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18346  }
18347  else
18348  {
18349  HString = AnsiString(HLoc);
18350  }
18351  if(VLoc < 0)
18352  {
18353  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18354  }
18355  else
18356  {
18357  VString = AnsiString(VLoc);
18358  }
18359  TLSTEntry.Location = HString + '-' + VString;
18360  }
18361  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
18362  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
18363  LocServiceTimesVector.push_back(TLSTEntry);
18364  }
18365  }
18366 
18367  else if(AVE.SequenceType == StartSequence) //other start entries, all located
18368  {
18369  TLSTEntry.Location = AVE.LocationName;
18370  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
18371  LocServiceTimesVector.push_back(TLSTEntry);
18372  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18373  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
18374  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18375  {
18376  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18377  {
18378  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
18379  break;
18380  }
18381  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered in a later test
18382  {
18383  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
18384  break;
18385  }
18386  }
18387  if(FoundStopTime == "")
18388  {
18389  throw Exception("Failure to determine FoundStopTime for SequenceType == StartSequence");
18390  }
18391  int WhileCount = 0;
18392  while(true)
18393  {
18394  //add minutes until reach FoundStopTime but don't add that time
18395  WhileCount++;
18396  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18397  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18398  TLSTEntry.DepTime = "";
18399  TLSTEntry.ArrTime = "";
18400  if(IncTime >= FoundStopTime) //don't add that time
18401  {
18402  break;
18403  }
18404  LocServiceTimesVector.push_back(TLSTEntry);
18405  if(WhileCount > 2000)
18406  {
18407  throw Exception("While loop failed to break in 2000 loops for SequenceType == StartSequence");
18408  }
18409  }
18410  }
18411 
18412  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
18413  {
18414  TLSTEntry.Location = AVE.LocationName;
18415  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
18416  {
18417  bool SkipAddingMinutes = false;
18418  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
18419  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
18420  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
18421  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
18422  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
18423  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
18424  {
18425  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
18426  {
18427  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
18428  break;
18429  }
18430  if(ActionVector.at(a).SequenceType == FinishSequence) //finish catered for in a later test
18431  {
18432  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
18433  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr != NULL) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr != NULL)))
18434  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
18435  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
18436  {
18437  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
18438  SkipAddingMinutes = true;
18439  }
18440  break;
18441  }
18442  }
18443  if(FoundStopTime == "")
18444  {
18445  throw Exception("Failure to determine FoundStopTime for SequenceType == StartSequence");
18446  }
18447  if(!SkipAddingMinutes)
18448  {
18449  int WhileCount = 0;
18450  while(true)
18451  {
18452  //add minutes until reach FoundStopTime but don't add that time
18453  WhileCount++;
18454  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18455  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
18456  TLSTEntry.DepTime = "";
18457  TLSTEntry.ArrTime = "";
18458  if(IncTime >= FoundStopTime) //don't add that time
18459  {
18460  break;
18461  }
18462  LocServiceTimesVector.push_back(TLSTEntry);
18463  if(WhileCount > 2000)
18464  {
18465  throw Exception("While loop failed to break in 2000 loops for SequenceType == StartSequence");
18466  }
18467  }
18468  }
18469  }
18470  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
18471  {
18472  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
18473  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
18474  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
18475  {
18476  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
18477  {
18478  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
18479  LocServiceTimesVector.pop_back();
18480  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
18481  }
18482  else //just add the dep & atloc times
18483  {
18484  TLSTEntry.ArrTime = "";
18485  LocServiceTimesVector.push_back(TLSTEntry);
18486  }
18487  }
18488  else //just add the dep & atloc times
18489  {
18490  TLSTEntry.ArrTime = "";
18491  LocServiceTimesVector.push_back(TLSTEntry);
18492  }
18493  }
18494  }
18495 
18496  else if(AVE.FormatType == TimeTimeLoc)
18497  {
18498  TLSTEntry.Location = AVE.LocationName;
18499  if(AVE.ArrivalTime > TDateTime(-1)) //should be
18500  {
18501  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
18502  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
18503  }
18504  if(AVE.DepartureTime > TDateTime(-1)) //should be
18505  {
18506  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
18507  }
18508  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
18509  {
18510  LocServiceTimesVector.push_back(TLSTEntry);
18511  }
18512  else
18513  {
18514  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
18515  TLSTEntry.DepTime = "";
18516  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
18517  TLSTEntry.ArrTime = ""; //done with this now
18518  while(TLSTEntry.AtLocTime < TempDepTime)
18519  {
18520  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18521  if(TLSTEntry.AtLocTime == TempDepTime)
18522  {
18523  TLSTEntry.DepTime = TempDepTime; //restore value
18524  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
18525  }
18526  else
18527  {
18528  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
18529  }
18530  }
18531  }
18532  }
18533 
18534  else if(AVE.FormatType == PassTime) //added at v2.9.1
18535  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
18536  TLSTEntry.Location = AVE.LocationName;;
18537  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
18538  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
18539  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
18540  TLSTEntry.ArrTime = ""; //need to reset this to null
18541  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
18542  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
18543  }
18544 
18545  else if(AVE.FormatType == ExitRailway) //Fer
18546  {
18547  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
18548  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
18549  //be wrong, but can't guess from here & most will have same name
18550  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
18551  if(LName != "")
18552  {
18553  TLSTEntry.Location = LName;
18554  }
18555  else
18556  {
18557  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
18558  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
18559  AnsiString HString;
18560  AnsiString VString;
18561  if(HLoc < 0)
18562  {
18563  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
18564  }
18565  else
18566  {
18567  HString = AnsiString(HLoc);
18568  }
18569  if(VLoc < 0)
18570  {
18571  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
18572  }
18573  else
18574  {
18575  VString = AnsiString(VLoc);
18576  }
18577  TLSTEntry.Location = HString + '-' + VString;
18578  }
18579  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
18580  }
18581 
18582  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
18583  {
18584  AnsiString FrhTime;
18585  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
18586  {
18587  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
18588  }
18589  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
18590  {
18591  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
18592  }
18593  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
18594  TLSTEntry.Location = AVE.LocationName;
18595  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18596  TLSTEntry.FrhMarker = "Frh";
18597  LocServiceTimesVector.push_back(TLSTEntry);
18598  TLSTEntry.FrhMarker = "";
18599  //add all times from next minute to end of timetable
18600  while(IncTime <= LastTTTime)
18601  {
18602  TLSTEntry.AtLocTime = IncTime;
18603  LocServiceTimesVector.push_back(TLSTEntry);
18604  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
18605  }
18606  }
18607 
18608  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
18609  {
18610  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
18611  {
18612  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
18613  TLSTEntry.Location = AVE.LocationName;
18614  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
18615  TLSTEntry.FrhMarker = "Frh";
18616  LocServiceTimesVector.push_back(TLSTEntry);
18617  TLSTEntry.FrhMarker = "";
18618  //add all times from next minute to end of timetable
18619  while(IncTime <= LastTTTime)
18620  {
18621  TLSTEntry.AtLocTime = IncTime;
18622  LocServiceTimesVector.push_back(TLSTEntry);
18623  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
18624  }
18625  }
18626  }
18627 
18628  else if(AVE.SequenceType == FinishSequence) //other finish types - all located & all link to another service
18629  {
18630  //nothing is done here as the entry will be listed at this time under the new service reference
18631  }
18632  }
18633  }
18634  }
18635  SequenceLog += "5\n";
18636  //now sort in location order
18637  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
18638  //LocServiceTimesVector now complete & sorted in location order
18639 
18640 /*
18641 //start of debugging section
18642 //create LocServiceTimesVector output file for debugging purposes
18643  AnsiString LSTVTestFile = CurDir + "\\Formatted timetables\\LSTVTestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
18644  std::ofstream LSTVFile(LSTVTestFile.c_str());
18645  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
18646  {
18647  LSTVFile << LSTVIt->Location + '\n';
18648  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
18649  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
18650  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
18651  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
18652  if(LSTVIt->FrhMarker == "")
18653  {
18654  LSTVFile << "Not Frh\n";
18655  }
18656  else
18657  {
18658  LSTVFile << LSTVIt->FrhMarker + '\n';
18659  }
18660  LSTVFile << '\n';
18661  }
18662  LSTVFile.close();
18663  Utilities->CallLogPop();
18664  return(true);
18665 //end of debugging section
18666 */
18667  //declare pointers for use in printouts
18668  TLocServiceTimesVector::iterator Ptr1, Ptr2;
18669 
18670  //set up the output file
18671  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18672  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
18673 
18674  std::ofstream TTFile3(TTFileName3.c_str());
18675 
18676  if(TTFile3 == 0)
18677  {
18678  ShowMessage("Conflict Analysis file failed to open - can't be created");
18679  Utilities->CallLogPop(2210);
18680  return(false);
18681  }
18682  if(LocServiceTimesVector.empty())
18683  {
18684  ShowMessage("No timetabled services found");
18685  TTFile3.close();
18686  DeleteFile(TTFileName3);
18687  Utilities->CallLogPop(2211);
18688  return(false);
18689  }
18690  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
18691  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
18692  SequenceLog += "6\n";
18693 
18694 /*
18695 //print out TrainDataVectorCopy & TrainDataVector for debugging purposes, TrainDataVectorCopy first
18696 
18697 // Double crosslink (shuttle) table:
18698 //Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
18699 // Code ShuttleLink- EntryPtr ShuttleLink-
18700 // HeadCode EntryPtr
18701 
18702 //Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
18703 //Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
18704 //F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
18705 //Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
18706 //Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
18707 //Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
18708 //
18709 //Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
18710 
18711 //NOTE: from above for F-nshs & Sns-fsh in the TrainDataVectorCopy the OtherLinkedHeadCode will be correct as it is derived from LinkTrainEntryPtr which is correct
18712 //but for the original TrainDataVector the OtherLinkedHeadCode will be incorrect, hence have to use the NonRepeatingShuttleLinkHeadCode as that is the correct one
18713 //these were errors when first coded but work ok, just keep in mind when making any changes
18714 
18715 std::ofstream TDVCFile((CurDir + "\\Formatted timetables\\Conflict Analysis " + TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss") + "; " + RailwayTitle + "; " + TimetableTitle + " TrainDataVectorCopy.txt").c_str());
18716 AnsiString OHC = "", NRHC = "";
18717 AnsiString OLk = "", NRLk = "";
18718 TDVCFile << "Note that for the TrainDataVectorCopy OH and OLk should always be the same, as OH is derived from OHLk; and similarly for NR and NRLk\n\n";
18719 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
18720 {
18721  TDVCFile << TDVCIt->ServiceReference + '\n';
18722  TDVCFile << TDVCIt->Description + '\n';
18723  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
18724  {
18725  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
18726  if(AVE.OtherHeadCode == "")
18727  {
18728  OHC = "OH 0";
18729  }
18730  else
18731  {
18732  OHC = "OH " + AVE.OtherHeadCode;
18733  }
18734  if(AVE.NonRepeatingShuttleLinkHeadCode == "")
18735  {
18736  NRHC = "NR 0";
18737  }
18738  else
18739  {
18740  NRHC = "NR " + AVE.NonRepeatingShuttleLinkHeadCode;
18741  }
18742  if(TDVCIt->ActionVector.at(x).LinkedTrainEntryPtr == 0)
18743  {
18744  OLk = "OLk 0";
18745  }
18746  else
18747  {
18748  OLk = "OLk " + TDVCIt->ActionVector.at(x).LinkedTrainEntryPtr->ServiceReference;
18749  }
18750  if(TDVCIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr == 0)
18751  {
18752  NRLk = "NRLk 0";
18753  }
18754  else
18755  {
18756  NRLk = "NRLk " + TDVCIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18757  }
18758 
18759  if(AVE.FormatType == TimeCmd) //cdt only
18760  {
18761  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << '\n';
18762  }
18763  if((AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle))
18764  {
18765  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18766  }
18767  else if((AVE.FormatType == FSHNewService) || (AVE.FormatType == SNSShuttle)) //these should have 2 linked services
18768  {
18769  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18770  }
18771  else if(AVE.FormatType == SNSNonRepeatFromShuttle)
18772  {
18773  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18774  }
18775  else if(AVE.FormatType == StartNew)
18776  {
18777  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt RearStartID " << Track->TrackElementAt(-1, AVE.RearStartOrRepeatMins).ElementID
18778  << " FrontStartID " << Track->TrackElementAt(-2, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18779  }
18780  else if(AVE.FormatType == SNTShuttle)
18781  {
18782  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt-sh RearStartID " << Track->TrackElementAt(-3, AVE.RearStartOrRepeatMins).ElementID
18783  << " FrontStartID " << Track->TrackElementAt(-4, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18784  }
18785  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18786  {
18787  TDVCFile << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
18788  }
18789  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18790  {
18791  TDVCFile << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
18792  }
18793  else if(AVE.FormatType == TimeTimeLoc)
18794  {
18795  TDVCFile << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
18796  }
18797  else if(AVE.FormatType == PassTime)
18798  {
18799  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18800  }
18801  else if(AVE.FormatType == ExitRailway)
18802  {
18803  TDVCFile << Utilities->Format96HHMM(AVE.EventTime) << " Fer" << '\n';
18804  }
18805  else if(AVE.FormatType == FinRemHere)
18806  {
18807  TDVCFile << "Frh" << '\n';
18808  }
18809  }
18810  TDVCFile << '\n';
18811 }
18812 TDVCFile.close();
18813 
18814 //print out original TrainDataVector for comparison
18815 std::ofstream TDVFile((CurDir + "\\Formatted timetables\\Conflict Analysis " + TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss") + "; " + RailwayTitle + "; " + TimetableTitle + " TrainDataVector.txt").c_str());
18816 TDVFile << "Note that in the TrainDataVector, non-repeating shuttle link services F-nshs and Sns-fsh use the non-repeating headcode (NR) values for the corresponding "
18817  "shuttle headcodes when it should be the other headcode (OH), and the other headcode is unused. The link values are the right way round. Also OH & NR "
18818  "values ARE headcodes and not service references, but OLk and NRLk values are service references.\n\n";
18819 //F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
18820 //Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
18821 for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18822 {
18823  TDVFile << TDVIt->ServiceReference + '\n';
18824  TDVFile << TDVIt->Description + '\n';
18825  for(unsigned int x = 0; x < TDVIt->ActionVector.size(); x++)
18826  {
18827  TActionVectorEntry AVE = TDVIt->ActionVector.at(x);
18828  if(AVE.OtherHeadCode == "")
18829  {
18830  OHC = "OH 0";
18831  }
18832  else
18833  {
18834  OHC = "OH " + AVE.OtherHeadCode;
18835  }
18836  if(AVE.NonRepeatingShuttleLinkHeadCode == "")
18837  {
18838  NRHC = "NR 0";
18839  }
18840  else
18841  {
18842  NRHC = "NR " + AVE.NonRepeatingShuttleLinkHeadCode;
18843  }
18844  if(TDVIt->ActionVector.at(x).LinkedTrainEntryPtr == 0)
18845  {
18846  OLk = "OLk 0";
18847  }
18848  else
18849  {
18850  OLk = "OLk " + TDVIt->ActionVector.at(x).LinkedTrainEntryPtr->ServiceReference;
18851  }
18852  if(TDVIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr == 0)
18853  {
18854  NRLk = "NRLk 0";
18855  }
18856  else
18857  {
18858  NRLk = "NRLk " + TDVIt->ActionVector.at(x).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
18859  }
18860 
18861  if(AVE.FormatType == TimeCmd) //cdt only
18862  {
18863  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << '\n';
18864  }
18865  if((AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle))
18866  {
18867  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18868  }
18869  else if((AVE.FormatType == FSHNewService) || (AVE.FormatType == SNSShuttle)) //these should have 2 linked services
18870  {
18871  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18872  }
18873  else if(AVE.FormatType == SNSNonRepeatFromShuttle)
18874  {
18875  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << OHC << ' ' << NRHC << ' ' << OLk << ' ' << NRLk << '\n';
18876  }
18877  else if(AVE.FormatType == StartNew)
18878  {
18879  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt RearStartID " << Track->TrackElementAt(-5, AVE.RearStartOrRepeatMins).ElementID
18880  << " FrontStartID " << Track->TrackElementAt(-6, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18881  }
18882  else if(AVE.FormatType == SNTShuttle)
18883  {
18884  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Snt-sh RearStartID " << Track->TrackElementAt(-7, AVE.RearStartOrRepeatMins).ElementID
18885  << " FrontStartID " << Track->TrackElementAt(-8, AVE.FrontStartOrRepeatDigits).ElementID << '\n';
18886  }
18887  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18888  {
18889  TDVFile << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
18890  }
18891  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18892  {
18893  TDVFile << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
18894  }
18895  else if(AVE.FormatType == TimeTimeLoc)
18896  {
18897  TDVFile << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
18898  }
18899  else if(AVE.FormatType == PassTime)
18900  {
18901  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18902  }
18903  else if(AVE.FormatType == ExitRailway)
18904  {
18905  TDVFile << Utilities->Format96HHMM(AVE.EventTime) << " Fer" << '\n';
18906  }
18907  else if(AVE.FormatType == FinRemHere)
18908  {
18909  TDVFile << "Frh" << '\n';
18910  }
18911  }
18912  TDVFile << '\n';
18913 }
18914 TDVFile.close();
18915 //end of debugging
18916 */
18917  //arrivals
18918  if(ArrChecked)
18919  {
18920  //sort in ArrTime order for each location
18921  Ptr1 = LocServiceTimesVector.begin();
18922  Ptr2 = Ptr1 + 1;
18923  while(Ptr2 != LocServiceTimesVector.end())
18924  {
18925  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
18926  {
18927  Ptr2++;
18928  if(Ptr2 == LocServiceTimesVector.end())
18929  {
18930  break;
18931  }
18932  }
18933  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
18934  Ptr1 = Ptr2; //first entry with next name
18935  if(Ptr2 != LocServiceTimesVector.end())
18936  {
18937  Ptr2++;
18938  }
18939  }
18940 
18941  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
18942 
18943  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
18944  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
18945  MinuteString = " minutes";
18946  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
18947  if(ArrRange == 1)
18948  {
18949  MinuteString = " minute";
18950  }
18951  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
18952  TTFile3 << ",Platforms,Trains\n\n";
18953 
18954  Ptr1 = LocServiceTimesVector.begin();
18955  Ptr2 = Ptr1 + 1;
18956  while(Ptr2 != LocServiceTimesVector.end())
18957  {
18958  PreviousService = "";
18959  NumTrainsAtLoc = 0;
18960  ServiceAndRepeatNumTotal = "";
18961  NumPlats = 0;
18962  NumPlatsAtThisLocCalculated = false;
18963  BasicTime = "";
18964  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
18965  {
18966  PreviousService = "";
18967  NumTrainsAtLoc = 0;
18968  ServiceAndRepeatNumTotal = "";
18969  NumPlats = 0;
18970  NumPlatsAtThisLocCalculated = false;
18971  BasicTime = "";
18972  Ptr1++;
18973  Ptr2++;
18974  if(Ptr2 == LocServiceTimesVector.end())
18975  {
18976  break;
18977  }
18978  }
18979  if(Ptr2 == LocServiceTimesVector.end())
18980  {
18981  break;
18982  }
18983  while(Ptr2->Location == Ptr1->Location)
18984  {
18985  PreviousService = "";
18986  NumTrainsAtLoc = 0;
18987  ServiceAndRepeatNumTotal = "";
18988  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
18989  if((Ptr1->Location == "") && (Ptr2->Location == ""))
18990  {
18991  break;
18992  }
18993  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
18994  {
18995  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
18996  Ptr1++;
18997  Ptr2++;
18998  if(Ptr2 == LocServiceTimesVector.end())
18999  {
19000  break;
19001  }
19002  if(Ptr2->Location != Ptr1->Location)
19003  {
19004  break;
19005  }
19006  }
19007  if(Ptr2 == LocServiceTimesVector.end())
19008  {
19009  break;
19010  }
19011  if(Ptr2->Location != Ptr1->Location)
19012  {
19013  break;
19014  }
19015  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
19016  {
19017  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
19018  {
19019  break;
19020  }
19021  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19022  {
19023  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
19024  NumPlatsAtThisLocCalculated = true;
19025  }
19026  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19027  {
19028  if(ServiceAndRepeatNumTotal == "")
19029  {
19030  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
19031  NumTrainsAtLoc = 1;
19032  }
19033  else
19034  {
19035  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
19036  }
19037  }
19038  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
19039  if(ServiceAndRepeatNumTotal == "")
19040  {
19041  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
19042  NumTrainsAtLoc = 1;
19043  }
19044  else
19045  {
19046  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
19047  }
19048  Ptr1 = Ptr2;
19049  Ptr2++;
19050  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
19051  {
19052  int MaxNumberOfSameDirections = 0;
19053  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
19054  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
19055  {
19056 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
19057  TTFile3.close();
19058  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
19059 // Utilities->CallLogPop(2224);
19060 // return false;
19061  }
19062  AnsiString Asterisk = "";
19063  if(MaxNumberOfSameDirections >= NumPlats)
19064  {
19065  Asterisk = "* ";
19066  }
19067  //print out a single line for number of trains at loc with all service refs
19068  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
19069  ArrivalsPrinted = true;
19070  ServiceAndRepeatNumTotal = "";
19071  }
19072  if(Ptr2 == LocServiceTimesVector.end())
19073  {
19074  break;
19075  }
19076  if(Ptr2->Location != Ptr1->Location)
19077  {
19078  break;
19079  }
19080  }
19081  if(Ptr2 == LocServiceTimesVector.end())
19082  {
19083  break;
19084  }
19085  }
19086  }
19087  if(!ArrivalsPrinted)
19088  {
19089  TTFile3 << "Nothing to report for arrivals";
19090  }
19091  TTFile3 << "\n\n";
19092  }
19093  //end of routine for arrivals
19094  SequenceLog += "7\n";
19095  //departures
19096  if(DepChecked)
19097  {
19098  //sort in DepTime order for each location
19099  Ptr1 = LocServiceTimesVector.begin();
19100  Ptr2 = Ptr1 + 1;
19101  while(Ptr2 != LocServiceTimesVector.end())
19102  {
19103  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
19104  {
19105  Ptr2++;
19106  if(Ptr2 == LocServiceTimesVector.end())
19107  {
19108  break;
19109  }
19110  }
19111  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
19112  Ptr1 = Ptr2; //first entry with next name
19113  if(Ptr2 != LocServiceTimesVector.end())
19114  {
19115  Ptr2++;
19116  }
19117  }
19118 
19119  //routine for departures - number of trains departing within the specified range with services listed at the end
19120  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
19121  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
19122  MinuteString = " minutes";
19123  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19124  if(DepRange == 1)
19125  {
19126  MinuteString = " minute";
19127  }
19128  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
19129  TTFile3 << ",Platforms,Trains\n\n";
19130 
19131  Ptr1 = LocServiceTimesVector.begin();
19132  Ptr2 = Ptr1 + 1;
19133  while(Ptr2 != LocServiceTimesVector.end())
19134  {
19135  PreviousService = "";
19136  NumTrainsAtLoc = 0;
19137  ServiceAndRepeatNumTotal = "";
19138  NumPlats = 0;
19139  NumPlatsAtThisLocCalculated = false;
19140  BasicTime = "";
19141  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19142  {
19143  PreviousService = "";
19144  NumTrainsAtLoc = 0;
19145  ServiceAndRepeatNumTotal = "";
19146  NumPlats = 0;
19147  NumPlatsAtThisLocCalculated = false;
19148  BasicTime = "";
19149  Ptr1++;
19150  Ptr2++;
19151  if(Ptr2 == LocServiceTimesVector.end())
19152  {
19153  break;
19154  }
19155  }
19156  if(Ptr2 == LocServiceTimesVector.end())
19157  {
19158  break;
19159  }
19160  while(Ptr2->Location == Ptr1->Location)
19161  {
19162  PreviousService = "";
19163  NumTrainsAtLoc = 0;
19164  ServiceAndRepeatNumTotal = "";
19165  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
19166  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19167  {
19168  break;
19169  }
19170  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
19171  {
19172  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
19173  Ptr1++;
19174  Ptr2++;
19175  if(Ptr2 == LocServiceTimesVector.end())
19176  {
19177  break;
19178  }
19179  if(Ptr2->Location != Ptr1->Location)
19180  {
19181  break;
19182  }
19183  }
19184  if(Ptr2 == LocServiceTimesVector.end())
19185  {
19186  break;
19187  }
19188  if(Ptr2->Location != Ptr1->Location)
19189  {
19190  break;
19191  }
19192  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
19193  {
19194  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
19195  {
19196  break;
19197  }
19198  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19199  {
19200  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
19201  NumPlatsAtThisLocCalculated = true;
19202  }
19203  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19204  {
19205  if(ServiceAndRepeatNumTotal == "")
19206  {
19207  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
19208  NumTrainsAtLoc = 1;
19209  }
19210  else
19211  {
19212  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
19213  }
19214  }
19215  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
19216  if(ServiceAndRepeatNumTotal == "")
19217  {
19218  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
19219  NumTrainsAtLoc = 1;
19220  }
19221  else
19222  {
19223  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
19224  }
19225  Ptr1 = Ptr2;
19226  Ptr2++;
19227  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
19228  {
19229  int MaxNumberOfSameDirections = 0;
19230  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
19231  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
19232  {
19233 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
19234  TTFile3.close();
19235  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
19236 // Utilities->CallLogPop(2225);
19237 // return false;
19238  }
19239  AnsiString Asterisk = "";
19240  if(MaxNumberOfSameDirections >= NumPlats)
19241  {
19242  Asterisk = "* ";
19243  }
19244  //print out a single line for number of trains at loc with all service refs
19245  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
19246  DeparturesPrinted = true;
19247  ServiceAndRepeatNumTotal = "";
19248  }
19249  if(Ptr2 == LocServiceTimesVector.end())
19250  {
19251  break;
19252  }
19253  if(Ptr2->Location != Ptr1->Location)
19254  {
19255  break;
19256  }
19257  }
19258  if(Ptr2 == LocServiceTimesVector.end())
19259  {
19260  break;
19261  }
19262  }
19263  }
19264  if(!DeparturesPrinted)
19265  {
19266  TTFile3 << "Nothing to report for departures";
19267  }
19268  TTFile3 << "\n\n";
19269  }
19270  //end of routine for departures
19271  SequenceLog += "8\n";
19272 
19273  //list trains at locations at same time
19274 
19275  if(AtLocChecked)
19276  {
19277  //sort in AtLocTime order for each location
19278  Ptr1 = LocServiceTimesVector.begin();
19279  Ptr2 = Ptr1 + 1;
19280  while(Ptr2 != LocServiceTimesVector.end())
19281  {
19282  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
19283  {
19284  Ptr2++;
19285  if(Ptr2 == LocServiceTimesVector.end())
19286  {
19287  break;
19288  }
19289  }
19290  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
19291  Ptr1 = Ptr2; //first entry with next name
19292  if(Ptr2 != LocServiceTimesVector.end())
19293  {
19294  Ptr2++;
19295  }
19296  }
19297 
19298  //print out simultaneous AtLocs (don't need range of times for AtLocs)
19299  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
19300  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
19301  TTFile3 << ",Platforms,Trains,\n\n";
19302  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
19303  Ptr1 = LocServiceTimesVector.begin();
19304  Ptr2 = Ptr1 + 1;
19305  while(Ptr2 != LocServiceTimesVector.end())
19306  {
19307  PreviousService = "";
19308  ServiceAndRepeatNumTotal = "";
19309  NumTrainsAtLoc = 0;
19310  NumPlats = 0;
19311  NumPlatsAtThisLocCalculated = false;
19312  FrhCount = 0;
19313 
19314  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
19315  {
19316  PreviousService = "";
19317  ServiceAndRepeatNumTotal = "";
19318  NumTrainsAtLoc = 0;
19319  NumPlats = 0;
19320  NumPlatsAtThisLocCalculated = false;
19321  FrhCount = 0;
19322  Ptr1++;
19323  Ptr2++;
19324  if(Ptr2 == LocServiceTimesVector.end())
19325  {
19326  break;
19327  }
19328  }
19329  if(Ptr2 == LocServiceTimesVector.end())
19330  {
19331  break;
19332  }
19333  while(Ptr2->Location == Ptr1->Location)
19334  {
19335  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
19336  {
19337  FrhCount++;
19338  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19339  }
19340  PreviousService = "";
19341  NumTrainsAtLoc = 0;
19342  ServiceAndRepeatNumTotal = "";
19343  if((Ptr1->Location == "") && (Ptr2->Location == ""))
19344  {
19345  break;
19346  }
19347  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
19348  {
19349  Ptr1++;
19350  if(Ptr1->FrhMarker == "Frh")
19351  {
19352  FrhCount++;
19353  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19354  }
19355  Ptr2++;
19356  if(Ptr2 == LocServiceTimesVector.end())
19357  {
19358  break;
19359  }
19360  if(Ptr2->Location != Ptr1->Location)
19361  {
19362  break;
19363  }
19364  }
19365  if(Ptr2 == LocServiceTimesVector.end())
19366  {
19367  break;
19368  }
19369  if(Ptr2->Location != Ptr1->Location)
19370  {
19371  break;
19372  }
19373  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
19374  {
19375  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
19376  {
19377  break;
19378  }
19379  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
19380  {
19381  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
19382  NumPlatsAtThisLocCalculated = true;
19383  }
19384  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
19385  {
19386  if(ServiceAndRepeatNumTotal == "")
19387  {
19388  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
19389  NumTrainsAtLoc = 1;
19390  }
19391  else
19392  {
19393  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
19394  }
19395  }
19396  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
19397  if(ServiceAndRepeatNumTotal == "")
19398  {
19399  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
19400  NumTrainsAtLoc = 1;
19401  }
19402  else
19403  {
19404  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
19405  }
19406  Ptr1 = Ptr2;
19407  if(Ptr1->FrhMarker == "Frh")
19408  {
19409  FrhCount++;
19410  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
19411  }
19412  Ptr2++;
19413  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
19414  {
19415 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
19416 //new text //don't print out if all remainers or if only 1 train at loc
19417  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
19418 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
19419 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
19420  {
19421  AnsiString Asterisk = "";
19422  if(NumTrainsAtLoc > NumPlats)
19423  {
19424  Asterisk = "* ";
19425  }
19426  //print out a single line for number of trains at loc with all service refs
19427  if(FrhCount == 0)
19428  {
19429  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
19430  }
19431  else if(FrhCount == 1)
19432  {
19433  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
19434  }
19435  else
19436  {
19437  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
19438  }
19439  LastFrhCount = FrhCount;
19440  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
19441  AtLocsPrinted = true;
19442  ServiceAndRepeatNumTotal = "";
19443  }
19444  }
19445  if(Ptr2 == LocServiceTimesVector.end())
19446  {
19447  break;
19448  }
19449  if(Ptr2->Location != Ptr1->Location)
19450  {
19451  break;
19452  }
19453  }
19454  if(Ptr2 == LocServiceTimesVector.end())
19455  {
19456  break;
19457  }
19458  }
19459  }
19460  if(!AtLocsPrinted)
19461  {
19462  TTFile3 << "Nothing to report for trains at locations";
19463  }
19464  TTFile3 << "\n\n";
19465  //end of simultaneous AtLocs
19466  }
19467  SequenceLog += "9\n";
19468 
19469 /*
19470 //start of debugging section
19471  //print out the full vector here for testing purposes
19472  TTFile3 << "Full LocServiceTimesVector\n\n";
19473  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
19474 
19475  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
19476  {
19477  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
19478  }
19479 
19480  TTFile3 << "\n\n\n";
19481 //end of debugging
19482 */
19483 
19484 /*cdt analysis - added at v2.10.0
19485 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
19486 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
19487 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
19488 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
19489 service.
19490 
19491 Use The TrainDataVectorCopy as that has all unique service refs.
19492 
19493 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
19494 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
19495 
19496 First create a new TrainDataVector from earlier copy as above with single services
19497 */
19498  if(DirChecked)
19499  {
19500  //direction analysis added at v2.10.0
19501  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
19502  TTrainDataVector SingleServiceVector, PartServiceVector;
19503 
19504  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
19505  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
19506  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
19507  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
19508  {
19510  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
19511  {
19512  TDE.ActionVector.erase(TDE.ActionVector.end() - 1); //strip repeat entry if present
19513  }
19514  const TActionVector &AV = TDE.ActionVector;
19515  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
19516  {
19517  SingleServiceEntry = TDE;
19518  TActionVector &SSAV = SingleServiceEntry.ActionVector;
19519  for(unsigned int y = 0; y < SSAV.size(); y++)
19520  {
19521  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
19522  {
19523  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
19524  break; //finished with this one
19525  }
19526  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
19527  {
19528  PartServiceEntry = TDE; //start with complete entry
19529  PartServiceEntry.ActionVector.clear(); //clear AV
19530  for(unsigned int z = 0; z <= y; z++)
19531  {
19532  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
19533  if(z == y)
19534  {
19535  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
19536  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
19537  }
19538  }
19539  PartServiceVector.push_back(PartServiceEntry);
19540  if(SSAV.at(y).Command == "fsp")
19541  {
19542  SSAV.at(y).Command = "Front split - original service continues below";
19543  SSAV.at(y).OtherHeadCode = "";
19544  }
19545  if(SSAV.at(y).Command == "rsp")
19546  {
19547  SSAV.at(y).Command = "Rear split - original service continues below";
19548  SSAV.at(y).OtherHeadCode = "";
19549  }
19550  //don't break & continue here because the original train carries on
19551  }
19552  else if(SSAV.at(y).Command == "Fns")
19553  {
19554  SSAV.at(y).Command = "chr-Fns";
19555  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
19556  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19557  break; //from y loop
19558  }
19559  else if(SSAV.at(y).Command == "Fns-sh")
19560  {
19561  SSAV.at(y).Command = "chr-Fns-sh";
19562  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19563  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
19564  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19565  break; //from y loop
19566  }
19567  else if(SSAV.at(y).Command == "F-nshs")
19568  {
19569  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
19570  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
19571  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
19572  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
19573  break; //from y loop
19574  }
19575  }
19576  }
19577  }
19578  SequenceLog += "10\n";
19579  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
19580 
19581  //Now add Sns & Sns-sh services to PartServiceVector entries
19582  AnsiString NextRef;
19583  while(!PartServiceVector.empty())
19584  {
19585  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
19586  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
19587  {
19588  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
19589  {
19590  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
19591  }
19592  }
19593  //find it in TrainDataVectorCopy
19594  bool FinishType = true, FoundFlag = false;
19595  while(FinishType)
19596  {
19597  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
19598  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
19599  if(FoundFlag)
19600  {
19601  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
19602  {
19603  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
19604  {
19605  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19606  }
19607  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
19608  {
19609  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19610  }
19611  else
19612  {
19613  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
19614  {
19615  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19616  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
19617  PartServiceVector.erase(PartServiceVector.begin());
19618  break; //from y loop
19619  }
19620  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
19621  {
19622  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
19623  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
19624  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
19625  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
19626  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
19627  if(TempEntry.ActionVector.at(y).Command == "fsp")
19628  {
19629  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
19630  TempEntry.ActionVector.at(y).OtherHeadCode = "";
19631  }
19632  if(TempEntry.ActionVector.at(y).Command == "rsp")
19633  {
19634  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
19635  TempEntry.ActionVector.at(y).OtherHeadCode = "";
19636  }
19637  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
19638  }
19639  else if(TempEntry.ActionVector.at(y).Command == "Fns")
19640  {
19641  TempEntry.ActionVector.at(y).Command = "chr-Fns";
19642  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19643  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
19644  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19645  break; //from y loop
19646  }
19647  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
19648  {
19649  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
19650  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19651  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
19652  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
19653  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19654  break; //from y loop
19655  }
19656  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
19657  {
19658  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
19659  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19660  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
19661  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
19662  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
19663  break; //from y loop
19664  }
19665  }
19666  }
19667  }
19668  else
19669  {
19670  SequenceLog += + "11\n";
19671  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
19672  }
19673  }
19674  }
19675  if(!PartServiceVector.empty())
19676  {
19677  SequenceLog += "12\n";
19678  throw Exception("PartServiceVector should be empty here - size = " + AnsiString(PartServiceVector.size()));
19679  }
19680  SequenceLog += "13\n";
19681  /*
19682  form:-
19683  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
19684  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
19685  then multiple entries, separated by commas, of the form:-
19686 
19687  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
19688  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
19689  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
19690 
19691  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
19692  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
19693  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
19694 
19695  HH:MM;Command (cdt) }TimeCmd }
19696  HH:MM;Command;new description (dsc) }TimeCmdDescription }
19697  HH:MM;Location (arr & dep) }TimeLoc }
19698  HH:MM;HH:MM;Location }TimeTimeLoc }
19699  HH:MM;pas;Location }PassTime }
19700  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
19701  HH:MM;Fer;set of allowable IDs }ExitRailway }
19702  Command (Frh only) }FinRemHere }
19703 
19704  R;mm;dd;nn. Repeat Repeat entry
19705 
19706  Formats:
19707 
19708  Command only: Frh
19709  Time;Command: cdt
19710  Time;Command;new description: dsc
19711  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
19712  Time;Command;2 Element IDs: Snt
19713  Time;Comand;n Element IDs: Fer
19714  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
19715  Time;Command;2 Element IDs;Headcode Snt-sh
19716  Time;Command;Location pas
19717  Time;Location Arr Dep
19718  Time;Time;Location Arr & dep together
19719  */
19720 
19721 /*
19722 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
19723 checking forwards until it comes to a continuation (no report), a location name that is not null and
19724 different to the train's front element name (whether null or not) (no report), a leading point
19725 (no report) or buffers (report).
19726 */
19727  bool BufferFacingUnReportedFlag = true;
19728  bool TrainFacingBuffersReported = false; //new flag added at v2.19.0 for 'no facing buffers' message instead of BufferFacingUnReportedFlag
19729  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19730  {
19731  TTrackElement ThisElement, NextElement;
19732  TTrainDataEntry TDE = SingleServiceVector.at(x);
19733  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
19734  {
19735  SequenceLog += "13a\n";
19736  throw Exception("Repeat entry present in SingleServiceVector at position " + AnsiString(x));
19737  }
19738  const TActionVector &AV = TDE.ActionVector;
19739  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
19740  {
19741  bool BufferFlag = false;
19742  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
19743  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
19744  AnsiString FrontLocName = AV.at(0).LocationName;
19745  int NextEntryPos, NextExitPos;
19746  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
19747  int ThisExitPos;
19748  if(ThisElement.Conn[0] == RearTVPos)
19749  {
19750  ThisExitPos = 1;
19751  }
19752  else if(ThisElement.Conn[1] == RearTVPos)
19753  {
19754  ThisExitPos = 0;
19755  }
19756  else if(ThisElement.Conn[2] == RearTVPos)
19757  {
19758  ThisExitPos = 3;
19759  }
19760  else if(ThisElement.Conn[3] == RearTVPos)
19761  {
19762  ThisExitPos = 2;
19763  }
19764  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
19765  {
19766  BufferFlag = true;
19767  }
19768  else //continue tracking forwards
19769  {
19770  while(true)
19771  {
19772  if(ThisElement.Conn[ThisExitPos] == -1)
19773  {
19774  SequenceLog = "13b\n";
19775  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
19776  }
19777  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
19778  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
19779  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
19780  {
19781  BufferFlag = false; //should already be false
19782  break;
19783  }
19784  else if(NextElement.TrackType == Continuation)
19785  {
19786  BufferFlag = false;
19787  break;
19788  }
19789  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
19790  {
19791  BufferFlag = false;
19792  break;
19793  }
19794  else if(NextElement.TrackType == Buffers)
19795  {
19796  BufferFlag = true;
19797  break;
19798  }
19799  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
19800  {
19801  ThisElement = NextElement;
19802  ThisExitPos = 0;
19803  continue;
19804  }
19805  else
19806  {
19807  if(NextEntryPos == 0)
19808  {
19809  NextExitPos = 1;
19810  }
19811  else if(NextEntryPos == 1)
19812  {
19813  NextExitPos = 0;
19814  }
19815  else if(NextEntryPos == 2)
19816  {
19817  NextExitPos = 3;
19818  }
19819  else if(NextEntryPos == 3)
19820  {
19821  NextExitPos = 2;
19822  }
19823  }
19824  ThisElement = NextElement;
19825  ThisExitPos = NextExitPos;
19826  }
19827  }
19828  if(BufferFlag)
19829  {
19830  if(BufferFacingUnReportedFlag)
19831  {
19832  TTFile3 << "Train facing direction on creation analysis:-\n\n";
19833  BufferFacingUnReportedFlag = false;
19834  }
19835  if(AV.at(1).Command != "cdt") //added at v2.19.0
19836  {
19837  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation with no immediate change of direction\n";
19838  TrainFacingBuffersReported = true; //added at v2.19.0
19839  }
19840  }
19841  }
19842  }
19843  if(!TrainFacingBuffersReported) //added at v2.19.0
19844  {
19845  TTFile3 << "Nothing to report for train facing directions\n\n";
19846  }
19847  else
19848  {
19849  TTFile3 << '\n';
19850  }
19851  SequenceLog += "13c\n";
19852 
19853  //Perform the missing cdt check. Check every entry similar to the check in SecondPassActions and if find any print out the full sequence and service entries
19854  AnsiString LocationNameToBeChecked = "";
19855  bool MissingcdtUnreportedFlag = true;
19856  TNumList MarkerList;
19857  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19858  {
19859  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
19860  unsigned int y = 0;
19861  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures won't be marked if not changed
19862  bool FullBreak = false;
19863  MarkerList.clear();
19864  // first discard unlocated Snt entries as they don't have location name set
19865  while((y < TDEntry.ActionVector.size()) && !FullBreak)
19866  // need to check each location name separately in turn, skipped for SignallerControl entries
19867  {
19868  if(/*(TDEntry.ActionVector.at(y).Command == "Fer") || */(TDEntry.ActionVector.at(y).FormatType == Repeat) ||
19869  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
19870  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
19871  {
19872  break; // out of the 'while' loop since have reached the end
19873  }
19874  LocationNameToBeChecked = ""; //this section where continuation name assigned for unlocated Snts added at v2.18.0
19875  if((TDEntry.ActionVector.at(y).Command == "Snt") && (TDEntry.ActionVector.at(y).LocationType == EnRoute)) //unlocated Snt - check if the continuation has a name
19876  {
19877  int EntryPos = TDEntry.ActionVector.at(0).RearStartOrRepeatMins; //this is a track vector position
19878  LocationNameToBeChecked = Track->TrackElementAt(1678, EntryPos).ActiveTrackElementName;
19879  }
19880  if(LocationNameToBeChecked == "")
19881  {
19882  if(y == 0) //unlocated and un-named Snt, so skip this value of y
19883  {
19884  y++;
19885  continue;
19886  }
19887  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName; //the only un-named values for ActionVectorEntry::LocationName
19888  } //are for unlocated Snts and Fers
19889  FirstInstance = y;
19890  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
19891  {
19892  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19893  if(/*(AVEntry.Command == "Fer") || */(AVEntry.FormatType == Repeat) ||
19894  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
19895  (AVEntry.Command == "Frh-sh"))
19896  {
19897  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
19898  }
19899  if(AVEntry.Command == "cdt")
19900  {
19901  break; // out of the 'z' loop since the check is only valid up to a change of direction
19902  }
19903  if(AVEntry.LocationName == LocationNameToBeChecked)
19904  {
19905  continue; // keep going while name same
19906  }
19907  if(AVEntry.LocationName != LocationNameToBeChecked)
19908  // if name different check forwards to see if repeats
19909  {
19910  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
19911  {
19912  AnsiString LocationName;
19913  if(TDEntry.ActionVector.at(a).Command == "cdt")
19914  {
19915  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
19916  }
19917  if(TDEntry.ActionVector.at(a).Command == "Fer") //this section where continuation name assigned for Fers added at v2.18.0
19918  {
19919  int ExitLoc = TDEntry.ActionVector.at(a).ExitList.front();
19920  LocationName = Track->TrackElementAt(1679, ExitLoc).ActiveTrackElementName;
19921  }
19922  else
19923  {
19924  LocationName = TDEntry.ActionVector.at(a).LocationName;
19925  }
19926  if(LocationName == LocationNameToBeChecked)
19927  {
19928  SecondInstance = a;
19929  AnsiString Sequence = TDEntry.ServiceReference;
19930  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
19931  {
19932  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
19933  {
19934  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
19935  }
19936  }
19937  if(MissingcdtUnreportedFlag)
19938  {
19939  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
19940  }
19941  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
19942  MarkerList.push_back(FirstInstance);
19943  MarkerList.push_back(SecondInstance);
19944  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
19945  MissingcdtUnreportedFlag = false;
19946  FullBreak = true; //no more checks for this sequence
19947  break; //out of the a & z loops
19948  }
19949  }
19950  break; // out of the 'z' loop since have checked 'a' as far as need to
19951  }
19952  }
19953  y++;
19954  }
19955  }
19956  if(MissingcdtUnreportedFlag)
19957  {
19958  TTFile3 << "Nothing to report for missing changes of direction\n\n";
19959  }
19960  else
19961  {
19962  TTFile3 << '\n';
19963  }
19964  SequenceLog += "14\n";
19965 
19966 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
19967  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
19968  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
19969  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
19970  names on one side of a cdt already checked either by the tt validator or the missing cdt check). Then compare the two lists and if any location
19971  included in both then ok, else report as questionable. If one list is empty then it is reported.
19972 */
19973  typedef std::list<AnsiString> TLocList;
19974  TLocList BackwardList, ForwardList;
19975  bool IntroLineNeeded = true;
19976  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
19977  {
19978  unsigned int cdtPosition = 9999;
19979  AnsiString cdtLocation = "";
19980  bool FoundSameName = false;
19981  bool FerEntry = false;
19982  MarkerList.clear();
19983  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
19984  for(unsigned int y = 0; y <= TDEntry.ActionVector.size(); y++) // <= because need to examine Fer endings after reached end of vector
19985  // need to check each location name separately in turn, skipped for SignallerControl entries
19986  {
19987  if((y == TDEntry.ActionVector.size()) && !FerEntry)
19988  {
19989  break;
19990  }
19991  BackwardList.clear();
19992  ForwardList.clear();
19993  bool ValidEnd = false;
19994  if(y < TDEntry.ActionVector.size())
19995  {
19996  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
19997  if((AVEntry.FormatType == Repeat) || //end of SSVector, shouldn't be any repeats
19998  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
19999  (AVEntry.Command == "Frh-sh"))
20000  {
20001  ValidEnd = true;
20002  }
20003  }
20004  if(FerEntry || ValidEnd)
20005  {
20006  if(MarkerList.empty())
20007  {
20008  break; // out of the 'y' loop since have reached the end & nothing to report
20009  }
20010  else
20011  {
20012  AnsiString Sequence = TDEntry.ServiceReference;
20013  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
20014  {
20015  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
20016  {
20017  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
20018  }
20019  }
20020  MarkerList.sort();
20021  if(IntroLineNeeded)
20022  {
20023  TTFile3 << "Questionable change of direction analysis.\n\n";
20024  TTFile3 << "For marked changes of direction there are no same-name locations listed both above (up to the start or another direction change)\n";
20025  TTFile3 << "and below (down to the end or another direction change) but not counting the change of direction location itself.\n\n";
20026  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
20027  TTFile3 << "make sure that none has been included incorrectly:\n\n";
20028  IntroLineNeeded = false;
20029  }
20030  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
20031  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
20032  break;
20033  }
20034  }
20035  if(y < TDEntry.ActionVector.size()) //if it is == ...size() then shouldn't have reached here
20036  {
20037  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
20038  if(AVEntry.Command != "cdt")
20039  {
20040  continue; //only looking for cdts
20041  }
20042  //here have found a cdt
20043  cdtPosition = y;
20044  cdtLocation = AVEntry.LocationName;
20045  for(int z = y - 1; z >= 0; z--)
20046  {
20047  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
20048  if(AVEntry2.Command == "cdt")
20049  {
20050  break; //don't look further back than the last cdt
20051  }
20052  AnsiString LocName = ""; //this section where continuation name assigned for unlocated Snts added at v2.18.0
20053  if((AVEntry2.Command == "Snt") && (AVEntry2.LocationType == EnRoute)) //unlocated Snt - check if the continuation has a name
20054  {
20055  int EntryPos = AVEntry2.RearStartOrRepeatMins; //this is a track vector position
20057  }
20058  if(LocName == "")
20059  {
20060  LocName = AVEntry2.LocationName; //the only un-named values for ActionVectorEntry::LocationName
20061  } //are for unlocated Snts and Fers
20062  if((LocName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
20063  {
20064  BackwardList.push_back(LocName);
20065  }
20066  }
20067  BackwardList.sort();
20068  BackwardList.unique();
20069  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
20070  {
20071  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
20072  if(/*(AVEntry3.Command == "Fer") || */(AVEntry3.FormatType == Repeat) ||
20073  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
20074  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
20075  {
20076  break; // out of the 'z' loop since have reached another cdt or the end
20077  }
20078  AnsiString LocName = "";
20079  if(AVEntry3.Command == "Fer") //this section where continuation name assigned for Fers added at v2.18.0
20080  {
20081  int ExitLoc = AVEntry3.ExitList.front();
20082  LocName = Track->TrackElementAt(1681, ExitLoc).ActiveTrackElementName;
20083  FerEntry = true;
20084  }
20085  else
20086  {
20087  LocName = AVEntry3.LocationName;
20088  }
20089  if((LocName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
20090  {
20091  ForwardList.push_back(LocName);
20092  }
20093  }
20094  ForwardList.sort();
20095  ForwardList.unique();
20096  FoundSameName = false;
20097  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
20098  if(!BackwardList.empty() && !ForwardList.empty())
20099  {
20100  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
20101  {
20102  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
20103  {
20104  if(*BLIt == *FLIt)
20105  {
20106  FoundSameName = true;
20107  }
20108  }
20109  }
20110  }
20111  if(!FoundSameName) //report the inability to find same name
20112  {
20113  MarkerList.push_back(cdtPosition);
20114  }
20115  }
20116  }
20117  }
20118  if(IntroLineNeeded)
20119  {
20120  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
20121  }
20122  else
20123  {
20124  TTFile3 << '\n';
20125  }
20126 /*
20127 //debug section
20128 //print all SSVector for diagnostic purposes
20129  TTFile3 << "Whole SSVector\n\n";
20130  TNumList EmptyList;
20131  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
20132  {
20133  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
20134  }
20135 //end of debug section
20136 */
20137  }
20138  SequenceLog += "15\n";
20139  TTFile3.close();
20140  Utilities->CallLogPop(2212);
20141  return(true);
20142  }
20143 
20144  catch(const Exception &e) //non error catch
20145  {
20146  AnsiString TTErrorFileName = "Analysis Error.txt";
20147  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
20148  std::ofstream TTError(TTErrorFileName.c_str());
20149  if(TTError == 0)
20150  {
20151  ShowMessage("Analysis error file failed to open - can't be created");
20152  Utilities->CallLogPop(2233);
20153  return(false);
20154  }
20155  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
20156  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
20157  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
20158 
20159  TTError.close();
20160  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
20161  Utilities->CallLogPop(2226);
20162  return(false);
20163  }
20164 }
20165 
20166 // ---------------------------------------------------------------------------
20167 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
20168 {
20169  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
20170  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
20171  {
20172  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
20173  }
20174  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
20175  {
20176  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
20177  AnsiString Marker = "";
20178  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
20179  {
20180  Marker = ',';
20181  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
20182  {
20183  if(int(x) == *MLIt)
20184  {
20185  Marker = "-->,";
20186  break;
20187  }
20188  }
20189  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
20190  if(AVE.FormatType == StartNew)
20191  {
20192  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
20193  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
20194  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
20195  }
20196  if(AVE.FormatType == SNTShuttle)
20197  {
20198  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
20199  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
20200  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
20201  }
20202  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
20203  {
20204  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
20205  }
20206  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
20207  {
20208  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
20209  }
20210  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
20211  {
20212  TActionVectorEntry AVHolder = AVE;
20213  if(AVE.Command.SubString(1,3) == "chr")
20214  {
20215  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
20216  {
20217  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
20218  AVE.OtherHeadCode = "";
20219  }
20220  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
20221  {
20222  AVE.Command = "Change of service to ";
20223  }
20224  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
20225  {
20226  AVE.Command = "Change to shuttle finishing service";
20227  }
20228  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
20229  {
20230  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
20231  AVE.OtherHeadCode = "";
20232  }
20233  }
20234  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
20235  AVE = AVHolder;
20236  }
20237  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
20238  {
20239  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << " Arr " << AVE.LocationName << '\n';
20240  }
20241  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
20242  {
20243  VecFile << Marker << Utilities->Format96HHMM(AVE.DepartureTime) << " Dep " << AVE.LocationName << '\n';
20244  }
20245  else if(AVE.FormatType == TimeTimeLoc)
20246  {
20247  VecFile << Marker << Utilities->Format96HHMM(AVE.ArrivalTime) << ' ' << Utilities->Format96HHMM(AVE.DepartureTime) << ' ' << AVE.LocationName << '\n';
20248  }
20249  else if(AVE.FormatType == PassTime)
20250  {
20251  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
20252  }
20253  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
20254  {
20255  AnsiString ListOfExits = "";
20256  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
20257  {
20258  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
20259  }
20260  VecFile << Marker << Utilities->Format96HHMM(AVE.EventTime) << " Fer " << ListOfExits <<'\n';
20261  }
20262  else if(AVE.FormatType == FinRemHere)
20263  {
20264  VecFile << Marker << "Frh" << '\n';
20265  }
20266  }
20267  VecFile << '\n';
20268  }
20269  Utilities->CallLogPop(2318);
20270 }
20271 
20272 // ---------------------------------------------------------------------------
20273 
20274 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString HeadCode, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
20275 {
20276  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + HeadCode);
20277  FoundFlag = false;
20278  FinishType = true;
20279  for(unsigned int x = 0; x < Vector.size(); x++)
20280  {
20281 // AnsiString ThisRef = Vector.at(x).ServiceReference;
20282  if(Vector.at(x).ServiceReference == HeadCode)
20283  {
20284  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
20285  {
20286  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
20287  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
20288  {
20289  FinishType = false;
20290  }
20291  }
20292  else
20293  {
20294  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
20295  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
20296  {
20297  FinishType = false;
20298  }
20299  }
20300  FoundFlag = true;
20301  Utilities->CallLogPop(2319);
20302  return(Vector.at(x));
20303  }
20304  }
20305  Utilities->CallLogPop(2320);
20306  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
20307 }
20308 
20309 // ---------------------------------------------------------------------------
20310 
20311 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
20312 {
20313 //convert times to integer minutes
20314  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
20315  if((Time1 == "") || (Time2 == ""))
20316  {
20317  Utilities->CallLogPop(2213);
20318  return(false);
20319  }
20320  int Mins = Time1.SubString(4,2).ToInt();
20321  int Hours = Time1.SubString(1,2).ToInt();
20322  int Time1Mins = (Hours * 60) + Mins;
20323  Mins = Time2.SubString(4,2).ToInt();
20324  Hours = Time2.SubString(1,2).ToInt();
20325  int Time2Mins = (Hours * 60) + Mins;
20326  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
20327  {
20328  Utilities->CallLogPop(2214);
20329  return(true);
20330  }
20331  Utilities->CallLogPop(2215);
20332  return(false);
20333 }
20334 
20335 // ---------------------------------------------------------------------------
20336 
20337 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
20338  bool &AnalysisError, int &MaxNumberOfSameDirections)
20339 {
20340  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
20341 
20342  try
20343  {
20344  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
20345  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
20346  int SCPos = 0;
20347  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
20348  //first change every second comma in Input to a semicolon so can separate services but keep times with services
20349  bool EvenComma = false;
20350  for(int x = 1; x <= Input.Length(); x++)
20351  {
20352  TempStr1 = Input[x];
20353  if(TempStr1 == AnsiString(',') && EvenComma)
20354  {
20355  TempStr2 += ';';
20356  }
20357  else
20358  {
20359  TempStr2 += Input[x];
20360  }
20361  if(TempStr1 == AnsiString(','))
20362  {
20363  EvenComma = !EvenComma;
20364  }
20365  }
20366  //load up the list of services with associated times
20367  while(TempStr2.Length() > 0)
20368  {
20369  SCPos = TempStr2.Pos(';');
20370  if(SCPos > 0) //0 if not found, as won't be when only one service left
20371  {
20372  OneService = TempStr2.SubString(1, SCPos - 1);
20373  ServiceList.push_back(OneService);
20374  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
20375  }
20376  else //no semicolon so looking at last (or only) element
20377  {
20378  ServiceList.push_back(TempStr2);
20379  TempStr2 = "";
20380  }
20381  }
20382  ServiceList.sort(); // alphabetical order
20383  ServiceList.unique(); //remove duplicates
20384  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
20385 
20386  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
20387  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
20388  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
20389  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
20390 
20391  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20392  {
20393  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
20394  }
20395  SLIt3 = ServiceList.end();
20396  SLIt3--; //so points to last element
20397  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
20398  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
20399  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
20400  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
20401  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
20402  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
20403 
20404  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
20405  {
20406  SLIt = SLIt1;
20407  SLIt++; //so points to one after SLIt1
20408  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
20409  {
20410  continue; //already allocated so skip to the next
20411  }
20412  else
20413  {
20414  CommaPos1 = SLIt1->Pos(','); //can't be 0
20415  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
20416  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
20417  SpacePos = ServiceRef1.Pos(' ');
20418  RepeatNum1 = 0;
20419  if(SpacePos > 0) //otherwise it's already correct
20420  {
20421  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
20422  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
20423  if(RepeatInfo1[1] == 'F')
20424  {
20425  RepeatNum1 = 0;
20426  }
20427  else
20428  {
20429  SpacePos = RepeatInfo1.Pos(' ');
20430  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
20431  }
20432  }
20433  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
20434  //but this includes the "&0" etc so need to strip these
20435  AmpersandPos = AnsiTime1.Pos('&');
20436  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
20437 
20438  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
20439  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
20440  {
20441  throw Exception("ASCLIt1 Error in " + Input);
20442  }
20443  ServiceCallingLocsList1 = ASCLIt1->second;
20444  AmpersandPos = SLIt1->Pos('&');
20445  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20446  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
20447 
20448  SameDirectionCount = 1;
20449  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
20450  {
20451  CommaPos2 = SLIt2->Pos(','); //can't be 0
20452  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
20453  //but this contains "(First service..." etc so need to strip these
20454  SpacePos = ServiceRef2.Pos(' ');
20455  RepeatNum2 = 0;
20456  if(SpacePos > 0) //otherwise it's already correct
20457  {
20458  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
20459  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
20460  if(RepeatInfo2[1] == 'F')
20461  {
20462  RepeatNum2 = 0;
20463  }
20464  else
20465  {
20466  SpacePos = RepeatInfo2.Pos(' ');
20467  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
20468  }
20469  }
20470  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
20471  //but this includes the "&0" etc so need to strip these
20472  AmpersandPos = AnsiTime2.Pos('&');
20473  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
20474 
20475  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
20476  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
20477  {
20478  throw Exception("ASCLIt2 Error in " + Input);
20479  }
20480  ServiceCallingLocsList2 = ASCLIt2->second;
20481  //now compare the two
20482  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
20483  {
20484  int AmpersandPos = SLIt2->Pos('&');
20485  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20486  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
20487  SameDirectionCount++;
20488  }
20489  }
20490  if(SameDirectionCount > MaxNumberOfSameDirections)
20491  {
20492  MaxNumberOfSameDirections = SameDirectionCount;
20493  }
20494  }
20495  }
20496 
20497  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
20498  {
20499  //any existing direction so allocate it now
20500  AmpersandPos = SLIt3->Pos('&');
20501  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
20502  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
20503  }
20504  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
20505  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20506  {
20507  //extract the DirectionMarker as an integer
20508  AmpersandPos = SLIt->Pos('&');
20509  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
20510  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
20511  DirectionMarker = DirectionMarkerString.ToInt();
20512  AnsiString DirectionSuffix = "";
20513  char c;
20514  if(DirectionMarker < 27)
20515  {
20516  c = 64 + DirectionMarker; //so 1 -> 'A'
20517  DirectionSuffix = "," + AnsiString(c);
20518  }
20519  else if(DirectionMarker < 53)
20520  {
20521  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
20522  DirectionSuffix = ",A" + AnsiString(c);
20523  }
20524  else
20525  {
20526  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
20527  }
20528  *SLIt = ServiceWithoutMarker + DirectionSuffix;
20529  }
20530  //now prepare the final consolidated output
20531  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20532  {
20533  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
20534  }
20535  if(Output.Length() > 0)
20536  {
20537  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
20538  }
20539  Utilities->CallLogPop(2216);
20540  return(Output);
20541  }
20542 
20543  catch(const Exception &e) //non error catch
20544  {
20545  AnalysisError = true;
20546  Utilities->CallLogPop(2227);
20547  return(e.Message);
20548  }
20549 }
20550 
20551 // ---------------------------------------------------------------------------
20552 
20553 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
20554 {
20555  //similar to above but doesn't include times in the input
20556  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
20557  AnsiString InternalInput = Input, Output = "", OneService = "";
20558  int CommaPos = 0;
20559  std::list<AnsiString> ServiceList;
20560  //load up the list
20561  while(InternalInput.Length() > 0)
20562  {
20563  CommaPos = InternalInput.Pos(',');
20564  if(CommaPos > 0) //0 if not found, as won't be when only one service left
20565  {
20566  OneService = InternalInput.SubString(1, CommaPos - 1);
20567  ServiceList.push_back(OneService);
20568  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
20569  }
20570  else //no comma so looking at last (or only) element
20571  {
20572  ServiceList.push_back(InternalInput);
20573  InternalInput = "";
20574  }
20575  }
20576 
20577  ServiceList.sort(); // alphabetical order
20578  ServiceList.unique(); //remove duplicates
20579  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
20580  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
20581  {
20582  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
20583  }
20584  if(Output.Length() > 0)
20585  {
20586  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
20587  }
20588  Utilities->CallLogPop(2217);
20589  return(Output);
20590 }
20591 
20592 // ---------------------------------------------------------------------------
20593 
20594 
20595 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
20596  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
20597 {
20598  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
20599  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
20600 
20601  std::list<AnsiString>::iterator LP1, LP2, ListPtr1, ListPtr2, LocPtr1, LocPtr2; //LP1 & 2 are temporary pointers, ListPtrs are
20602  //general list pointers, LocPtrs point to Location in the two lists
20603 
20604  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
20605  //for List1
20606  bool LocFound = false;
20607  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
20608  int IncMinutes;
20609  TDateTime FirstServiceTime;
20610 
20611  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
20612  int Ref1Target = 0, Ref1Count = 0;
20613  int Ref2Target = 0, Ref2Count = 0;
20614 
20615 /* drop this after retained slashes in ServiceRef
20616  int SlashPos = Ref1.Pos('/');
20617  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
20618  {
20619  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
20620  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
20621  }
20622  int Ref2Target = 0, Ref2Count = 0;
20623  SlashPos = Ref2.Pos('/');
20624  if(SlashPos > 0) //if 0 leave as is
20625  {
20626  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
20627  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
20628  }
20629 */
20630 
20631  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
20632  {
20633  //even if others have same names. But if there are cdt's then need to refind the correct service
20634  if((*ListPtr1) == Location) //
20635  {
20636  LocPtr1 = ListPtr1; //may be modified later
20637  LocFound = true;
20638  }
20639  if(ListPtr1->SubString(1, 3) == "%%%")
20640  {
20641  AnsiString CDTTime = ListPtr1->SubString(4, 5);
20642  //now adjust the time to correspond to the repeat if there is one
20643  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
20644  {
20645  IncMinutes = -1;
20646  FirstServiceTime = TDateTime(-1);
20647  bool BreakFlag = false;
20648  for(TTrainDataVector::iterator TDVIt = TrainDataVectorCopy.begin(); TDVIt != TrainDataVectorCopy.end(); TDVIt++)
20649  {
20650  if(TDVIt->ServiceReference == Ref1)
20651  {
20652  if(Ref1Target > Ref1Count)
20653  {
20654  Ref1Count++;
20655  continue;
20656  }
20657  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
20658  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
20659  {
20660  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
20661  {
20662  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
20663  BreakFlag = true;
20664  break;
20665  }
20666  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
20667  {
20668  FirstServiceTime = AVIt->ArrivalTime;
20669  BreakFlag = true;
20670  break;
20671  }
20672  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
20673  {
20674  FirstServiceTime = AVIt->DepartureTime;
20675  BreakFlag = true;
20676  break;
20677  }
20678  }
20679  if(BreakFlag)
20680  {
20681  break;
20682  }
20683  }
20684  }
20685  if(IncMinutes == -1)
20686  {
20687  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20688  }
20689  if(FirstServiceTime == TDateTime(-1))
20690  {
20691  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20692  }
20693  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
20694  }
20695  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
20696  {
20697  LocFound = false;
20698  continue;
20699  }
20700  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
20701  {
20702  break;
20703  }
20704  if(Time1 > CDTTime) //not there yet so go on
20705  {
20706  LocFound = false;
20707  continue;
20708  }
20709  if(Time1 < CDTTime) //gone too far so can stop now
20710  {
20711  break;
20712  }
20713  }
20714  }
20715  if(!LocFound) //have to find it in both lists
20716  {
20717  Utilities->CallLogPop(2228);
20718  return( false);
20719  }
20720  //for List2
20721  LocFound = false;
20722  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
20723  {
20724  if((*ListPtr2) == Location)
20725  {
20726  LocPtr2 = ListPtr2; //may be modified later
20727  LocFound = true;
20728  }
20729  if(ListPtr2->SubString(1, 3) == "%%%")
20730  {
20731  AnsiString CDTTime = ListPtr2->SubString(4, 5);
20732  //now adjust the time to correspond to the repeat if there is one
20733  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
20734  {
20735  IncMinutes = -1;
20736  FirstServiceTime = TDateTime(-1);
20737  bool BreakFlag = false;
20738  for(TTrainDataVector::iterator TDVIt = TrainDataVectorCopy.begin(); TDVIt != TrainDataVectorCopy.end(); TDVIt++)
20739  {
20740  if(TDVIt->ServiceReference == Ref2)
20741  {
20742  if(Ref2Target > Ref2Count)
20743  {
20744  Ref2Count++;
20745  continue;
20746  }
20747  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
20748  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
20749  {
20750  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
20751  {
20752  FirstServiceTime = AVIt->EventTime;
20753  BreakFlag = true;
20754  break;
20755  }
20756  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
20757  {
20758  FirstServiceTime = AVIt->ArrivalTime;
20759  BreakFlag = true;
20760  break;
20761  }
20762  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
20763  {
20764  FirstServiceTime = AVIt->DepartureTime;
20765  BreakFlag = true;
20766  break;
20767  }
20768  }
20769  if(BreakFlag)
20770  {
20771  break;
20772  }
20773  }
20774  }
20775  if(IncMinutes == -1)
20776  {
20777  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20778  }
20779  if(FirstServiceTime == TDateTime(-1))
20780  {
20781  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
20782  }
20783  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
20784  }
20785  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
20786  {
20787  LocFound = false;
20788  continue;
20789  }
20790  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
20791  {
20792  break;
20793  }
20794  if(Time2 > CDTTime) //not there yet so go on
20795  {
20796  LocFound = false;
20797  continue;
20798  }
20799  if(Time2 < CDTTime) //gone too far so can stop now
20800  {
20801  break;
20802  }
20803  }
20804  }
20805  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
20806  {
20807  Utilities->CallLogPop(2229);
20808  return( false);
20809  }
20810  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
20811  //set ListPtr1 to the search start position
20812  if(Arrival)
20813  {
20814  LP1 = List1.begin();
20815  LP1--; //now points to before the first entry
20816  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
20817  {
20818  if(ListPtr1 == List1.begin())
20819  {
20820  break;
20821  }
20822  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
20823  {
20824  ListPtr1++; //point to one past the cdt
20825  break;
20826  }
20827  }
20828  //set ListPtr2 to the search start position
20829  LP2 = List2.begin();
20830  LP2--; //now points to before the first entry
20831  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
20832  {
20833  if(ListPtr2 == List2.begin())
20834  {
20835  break;
20836  }
20837  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
20838  {
20839  ListPtr2++; //point to one past the cdt
20840  break;
20841  }
20842  }
20843  //ListPtr1 & 2 now at search start position
20844  LP1 = ListPtr1;
20845  LP2 = ListPtr2;
20846  //now search forwards, i.e. for common locations before Location
20847  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
20848  {
20849  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
20850  {
20851  break;
20852  }
20853  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
20854  {
20855  break;
20856  }
20857  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
20858  {
20859  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
20860  {
20861  break;
20862  }
20863  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
20864  {
20865  break;
20866  }
20867  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
20868  {
20869  Utilities->CallLogPop(2230);
20870  return( true);
20871  }
20872  }
20873  }
20874  }
20875 
20876  //now, for the departure analysis, reset the start positions and search locations after Location
20877 
20878  else
20879  {
20880  LP1 = LocPtr1;
20881  LP1++; //start at one past the location itself
20882  LP2 = LocPtr2;
20883  LP2++;
20884  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
20885  {
20886  if(ListPtr1 == List1.end()) //reached end point so stop
20887  {
20888  break;
20889  }
20890  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
20891  {
20892  break;
20893  }
20894  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
20895  {
20896  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
20897  {
20898  break;
20899  }
20900  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
20901  {
20902  break;
20903  }
20904  if((*ListPtr1) == (*ListPtr2)) //found a common later location
20905  {
20906  Utilities->CallLogPop(2231);
20907  return( true);
20908  }
20909  }
20910  }
20911  }
20912  Utilities->CallLogPop(2232);
20913  return( false);
20914 }
20915 
20916 // ---------------------------------------------------------------------------
20917 
20918 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
20919 {
20920  // changed at v2.7.0 to show allowable exit elements
20921  if(ExitList.empty())
20922  {
20923  return("");
20924  }
20925  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
20926  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
20927  AnsiString ExitLocList = "";
20928  AllowedExits = "";
20929 
20930  unsigned int Counter = 0;
20931  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
20932  {
20933  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
20934  Counter++;
20935  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
20936  {
20937  ExitLocList += "\n";
20938  }
20939  }
20940  if(StartName == "")
20941  {
20942  if(ExitList.size() == 1)
20943  {
20944  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
20945  Utilities->CallLogPop(1571);
20946  return(" at " + ID);
20947  }
20948  else
20949  {
20950  Utilities->CallLogPop(1572);
20951  if(ExitList.size() < 4)
20952  {
20953  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20954  return("");
20955  }
20956  else
20957  {
20958  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20959  return("");
20960  }
20961  }
20962  }
20963  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
20964  {
20965  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
20966  {
20967  Utilities->CallLogPop(1570);
20968  if(ExitList.size() < 4)
20969  {
20970  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20971  return("");
20972  }
20973  else
20974  {
20975  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20976  return("");
20977  }
20978  }
20979  }
20980  Utilities->CallLogPop(1569);
20981  if(ExitList.size() < 4)
20982  {
20983  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
20984  return(" at " + StartName);
20985  }
20986  else
20987  {
20988  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
20989  return(" at " + StartName);
20990  }
20991 }
20992 
20993 // ---------------------------------------------------------------------------
20994 /* can't trust this as locations within a vector may not be contiguous
20995  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
20996  {
20997  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
20998  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
20999  //must be preceded by a TimeLoc departure
21000  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
21001  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
21002  {
21003  if((AVPtr + x) < TDEPtr->ActionVector.end())
21004  {
21005  AnsiString xx = (AVPtr + x)->Command;//test
21006  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
21007  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
21008  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
21009  {
21010  Utilities->CallLogPop();
21011  return false;
21012  }
21013  else if((AVPtr + x)->SequenceType == FinishSequence)
21014  {
21015  Utilities->CallLogPop();
21016  return true;
21017  }
21018  }
21019  }
21020  Utilities->CallLogPop();
21021  return false;
21022  }
21023 */
21024 // ---------------------------------------------------------------------------
21025 
21026 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
21027 {
21028  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
21029  AnsiString FormatStr = "####0.0";
21030  AnsiString AvLateArrMins = "";
21031  AnsiString AvEarlyArrMins = "";
21032  AnsiString AvLatePassMins = "";
21033  AnsiString AvEarlyPassMins = "";
21034  AnsiString AvLateDepMins = "";
21035  AnsiString AvLateExitMins = "";
21036  AnsiString AvEarlyExitMins = "";
21037 
21038  //calculate remaining CumulativeDelayedRandMinsAllTrains for trains still in vector (CumulativeDelayedRandMinsAllTrains for exited or removed trains already accounted for)
21039  for(unsigned int x = 0; x < TrainVector.size(); x++)
21040  {
21041  Utilities->CumulativeDelayedRandMinsAllTrains += int(TrainVectorAt(89, x).CumulativeDelayedRandMinsOneTrain);
21042  }
21043 
21044  if(LateArrivals > 0)
21045  {
21046  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
21047  }
21048  if(EarlyArrivals > 0)
21049  {
21050  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
21051  }
21052  if(LatePasses > 0)
21053  {
21054  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
21055  }
21056  if(EarlyPasses > 0)
21057  {
21058  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
21059  }
21060  if(LateDeps > 0)
21061  {
21062  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
21063  }
21064  if(LateExits > 0) //added at v2.9.1
21065  {
21066  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
21067  }
21068  if(EarlyExits > 0) //added at v2.9.1
21069  {
21070  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
21071  }
21072  PerfFile << '\n' << '\n' << "***************************************";
21073  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
21074 
21075  if(OnTimeArrivals != 1)
21076  {
21077  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
21078  }
21079  else
21080  {
21081  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
21082  }
21083  if(LateArrivals > 1)
21084  {
21085  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
21086  }
21087  else if(LateArrivals == 1)
21088  {
21089  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
21090  }
21091  else
21092  {
21093  PerfFile << LateArrivals << " late arrivals" << '\n';
21094  }
21095  if(EarlyArrivals > 1)
21096  {
21097  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
21098  }
21099  else if(EarlyArrivals == 1)
21100  {
21101  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
21102  }
21103  else
21104  {
21105  PerfFile << EarlyArrivals << " early arrivals" << '\n';
21106  }
21107  if(OnTimePasses != 1)
21108  {
21109  PerfFile << OnTimePasses << " on-time passes" << '\n';
21110  }
21111  else
21112  {
21113  PerfFile << OnTimePasses << " on-time pass" << '\n';
21114  }
21115  if(LatePasses > 1)
21116  {
21117  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
21118  }
21119  else if(LatePasses == 1)
21120  {
21121  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
21122  }
21123  else
21124  {
21125  PerfFile << LatePasses << " late passes" << '\n';
21126  }
21127  if(EarlyPasses > 1)
21128  {
21129  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
21130  }
21131  else if(EarlyPasses == 1)
21132  {
21133  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
21134  }
21135  else
21136  {
21137  PerfFile << EarlyPasses << " early passes" << '\n';
21138  }
21139 
21140  if(OnTimeExits != 1) //this batch added at v2.9.1
21141  {
21142  PerfFile << OnTimeExits << " on-time exits" << '\n';
21143  }
21144  else
21145  {
21146  PerfFile << OnTimeExits << " on-time exit" << '\n';
21147  }
21148  if(LateExits > 1)
21149  {
21150  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
21151  }
21152  else if(LateExits == 1)
21153  {
21154  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
21155  }
21156  else
21157  {
21158  PerfFile << LateExits << " late exits" << '\n';
21159  }
21160  if(EarlyExits > 1)
21161  {
21162  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
21163  }
21164  else if(EarlyExits == 1)
21165  {
21166  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
21167  }
21168  else
21169  {
21170  PerfFile << EarlyExits << " early exits" << '\n';
21171  }
21172 
21173  if(OnTimeDeps != 1)
21174  {
21175  PerfFile << OnTimeDeps << " on-time departures" << '\n';
21176  }
21177  else
21178  {
21179  PerfFile << OnTimeDeps << " on-time departure" << '\n';
21180  }
21181  if(LateDeps > 1)
21182  {
21183  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
21184  }
21185  else if(LateDeps == 1)
21186  {
21187  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
21188  }
21189  else
21190  {
21191  PerfFile << LateDeps << " late departures" << '\n';
21192  }
21193  TDateTime TempExcessLCDownTime;
21194  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
21195  {
21196 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
21197  //later perf summaries with lower values, changed at v2.8.0
21198 // {
21199  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
21200 // }
21201 /*
21202  else
21203  {
21204  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
21205  }
21206 */
21207  if(TempExcessLCDownTime > TDateTime(0))
21208  {
21209  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
21210  }
21211  }
21212 
21213  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
21214 
21215  if(ExcessLCDownMins > 0.1)
21216  {
21217  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
21218  }
21219  else
21220  {
21221  ExcessLCDownMins = 0; //added at v2.16.1 so doesn't count towards performance score if < 0.1mins (else can have low score with no excess mins recorded)
21222  }
21223  if(Utilities->CumulativeDelayedRandMinsAllTrains > 0) //added at v2.13.0
21224  {
21225  PerfFile << Utilities->CumulativeDelayedRandMinsAllTrains << " minutes lost due to random delays when stopped at locations" << '\n';
21226  }
21227  if(MissedStops != 1)
21228  {
21229  PerfFile << MissedStops << " missed stops" << '\n';
21230  }
21231  else
21232  {
21233  PerfFile << MissedStops << " missed stop" << '\n';
21234  }
21235  if(OtherMissedEvents != 1)
21236  {
21237  PerfFile << OtherMissedEvents << " other missed events" << '\n';
21238  }
21239  else
21240  {
21241  PerfFile << OtherMissedEvents << " other missed event" << '\n';
21242  }
21243  if(SkippedTTEvents != 1)
21244  {
21245  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
21246  }
21247  else
21248  {
21249  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
21250  }
21251  if(UnexpectedExits != 1)
21252  {
21253  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
21254  }
21255  else
21256  {
21257  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
21258  }
21259  if(IncorrectExits != 1)
21260  {
21261  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
21262  }
21263  else
21264  {
21265  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
21266  }
21267  if(NumFailures != 1)
21268  {
21269  PerfFile << NumFailures << " train failures" << '\n';
21270  }
21271  else
21272  {
21273  PerfFile << NumFailures << " train failure" << '\n';
21274  }
21275  if(AvHoursIntValue > 0)
21276  {
21277  if(AvHoursIntValue == 1)
21278  {
21279  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
21280  }
21281  else
21282  {
21283  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
21284  }
21285  }
21286  AnsiString AvLateMinsLocsNotReached = "";
21287 
21289  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
21290  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
21291 
21292  if(LocsNotReached > 0)
21293  {
21294  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
21295  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
21296  }
21297  if(SPADRisks != 1)
21298  {
21299  PerfFile << SPADRisks << " SPAD risks" << '\n';
21300  }
21301  else
21302  {
21303  PerfFile << SPADRisks << " SPAD risk" << '\n';
21304  }
21305  if(SPADEvents != 1)
21306  {
21307  PerfFile << SPADEvents << " SPADs" << '\n';
21308  }
21309  else
21310  {
21311  PerfFile << SPADEvents << " SPAD" << '\n';
21312  }
21313  if(Derailments != 1)
21314  {
21315  PerfFile << Derailments << " derailments" << '\n';
21316  }
21317  else
21318  {
21319  PerfFile << Derailments << " derailment" << '\n';
21320  }
21321  if(CrashedTrains != 1)
21322  {
21323  PerfFile << CrashedTrains << " crashed trains" << '\n';
21324  }
21325  else
21326  {
21327  PerfFile << CrashedTrains << " crashed train" << '\n';
21328  }
21329  PerfFile << '\n' << "***************************************" << '\n';
21330 
21331  bool DerailSPADFlag = false, CrashFlag = false;
21332 
21333  int OverallScorePercent = 100;
21334  int TotArrDepExit = 0;
21335  double TotLateMinsFactor = 1;
21336  double MissedStopAndSPADRiskFactor = 1;
21337  double NetNegFactor = 1;
21338 
21340  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
21341  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
21342  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
21343  // 'no timetabled departures... message, which was inappropriate
21344 
21345  if((SPADEvents > 0) || (Derailments > 0))
21346  {
21347  OverallScorePercent = 5; // overrides other calculations
21348  DerailSPADFlag = true;
21349  }
21350  if(CrashedTrains > 0)
21351  {
21352  OverallScorePercent = 0; // overrides other calculations
21353  CrashFlag = true;
21354  }
21355  if(OverallScorePercent == 100)
21356  {
21357  int LatenessPenalty = TotLateArrMins + TotLateDepMins; //added at v2.13.0 for random delays
21358  if(Utilities->CumulativeDelayedRandMinsAllTrains > LatenessPenalty)
21359  {
21360  LatenessPenalty = 0;
21361  }
21362  else
21363  {
21364  LatenessPenalty -= Utilities->CumulativeDelayedRandMinsAllTrains;
21365  }
21366  if(TotArrDepExit > 0)
21367  {
21368  TotLateMinsFactor = exp((-0.1732) * (LatenessPenalty + OperatingTrainLateMins + NotStartedTrainLateMins + TotLateExitMins +
21369  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
21370  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
21371  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
21372  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
21373  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
21374  // of arrivals & departures, where 4% = half, 8% = a quarter etc
21375  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
21376  // NetNegfactor: product of the above two
21377  OverallScorePercent = 100 * NetNegFactor;
21378  }
21379  }
21380  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
21381  // flag condits added at v1.1.4 - see above for what the error was
21382  {
21383  AnsiString OneFailureString = ", though the failure would account for some poor performance";
21384  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
21385  AnsiString AddedString = "";
21386  if(NumFailures == 1)
21387  {
21388  AddedString = OneFailureString;
21389  }
21390  if(NumFailures > 1)
21391  {
21392  AddedString = TwoOrMoreFailureString;
21393  }
21394  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
21395  AnsiString Rating = "";
21396  if(OverallScorePercent == 100)
21397  {
21398  Rating = "Perfect!";
21399  }
21400  else if(OverallScorePercent >= 95)
21401  {
21402  Rating = "Excellent";
21403  }
21404  else if(OverallScorePercent >= 90)
21405  {
21406  Rating = "Very good";
21407  }
21408  else if(OverallScorePercent >= 80)
21409  {
21410  Rating = "Good";
21411  }
21412  else if(OverallScorePercent >= 70)
21413  {
21414  Rating = "Fair";
21415  }
21416  else if(OverallScorePercent >= 60)
21417  {
21418  Rating = "Unacceptable" + AddedString;
21419  }
21420  else if(OverallScorePercent >= 50)
21421  {
21422  Rating = "Poor" + AddedString;
21423  }
21424  else if(OverallScorePercent >= 40)
21425  {
21426  Rating = "Bad" + AddedString;
21427  }
21428  else if(OverallScorePercent >= 30)
21429  {
21430  Rating = "Very bad" + AddedString;
21431  }
21432  else if(OverallScorePercent >= 20)
21433  {
21434  Rating = "Terrible" + AddedString;
21435  }
21436  else if(OverallScorePercent >= 10)
21437  {
21438  Rating = "Appalling" + AddedString;
21439  }
21440  else if(OverallScorePercent >= 5)
21441  {
21442  if(DerailSPADFlag)
21443  {
21444  Rating = "Disastrous - potential loss of life";
21445  }
21446  // SPADs/Derailments
21447  else
21448  {
21449  Rating = "Dire" + AddedString;
21450  }
21451  }
21452  else if(OverallScorePercent < 5)
21453  {
21454  if(CrashFlag)
21455  {
21456  Rating = "Catastrophic - loss of life"; // Crashes
21457  }
21458  else
21459  {
21460  Rating = "Abysmal";
21461  }
21462  }
21463  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
21464  }
21465  else
21466  {
21467  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
21468  }
21469  PerfFile << '\n' << "***************************************";
21470  PerfFile.flush();
21471  Utilities->CallLogPop(1736);
21472 }
21473 
21474 // ---------------------------------------------------------------------------
21475 
21477 {
21478  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
21479  for(unsigned int x = 0; x < TrainVector.size(); x++)
21480  {
21481  TTrain &Train = TrainVectorAt(58, x);
21482  if(Train.Crashed)
21483  // can't use background colours for crashed & derailed because same colour
21484  {
21485  CrashWarning = true;
21486  }
21487  else if(Train.Derailed)
21488  // can't use background colours for crashed & derailed because same colour
21489  {
21490  DerailWarning = true;
21491  }
21492  else if(Train.BackgroundColour == clSPADBackground)
21493  // use colour as that changes as soon as passes signal
21494  {
21495  SPADWarning = true;
21496  }
21497  else if(Train.BackgroundColour == clTrainFailedBackground)
21498  {
21499  TrainFailedWarning = true;
21500  }
21501  else if(Train.BackgroundColour == clCallOnBackground)
21502  // use colour as also stopped at signal
21503  {
21504  CallOnWarning = true;
21505  }
21506  else if(Train.BackgroundColour == clSignalStopBackground)
21507  // use colour to distinguish from call-on
21508  {
21509  SignalStopWarning = true;
21510  }
21511  else if(Train.BackgroundColour == clBufferAttentionNeeded)
21512  // use colour to distinguish from ordinary buffer stop
21513  {
21514  BufferAttentionWarning = true;
21515  }
21516  }
21517  Utilities->CallLogPop(1796);
21518 }
21519 
21520 // ---------------------------------------------------------------------------
21521 
21523 {
21524  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
21525 
21526  // calculate lateness for running trains
21529  for(unsigned int x = 0; x < TrainVector.size(); x++)
21530  {
21531  TTrain &Train = TrainVectorAt(64, x);
21532  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
21533  AVEntryPtr++)
21534  {
21535  if(AVEntryPtr < Train.ActionVectorEntryPtr)
21536  {
21537  continue;
21538  }
21539  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.RevisedStoppedAtLoc() && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
21540  TTClockTime))
21541  {
21542  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
21544  }
21545 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
21546  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
21547  TTClockTime))
21548  {
21549  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
21550  OperatingTrainArrDep++;
21551  }
21552 */
21553  }
21554  }
21555 
21556  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
21559 
21560  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
21561  {
21562  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
21563  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
21564  int IncrementalMinutes = 0;
21565  if(AVEntryLast.FormatType == Repeat)
21566  {
21567  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
21568  }
21569  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
21570  {
21571  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
21572  if(TTOD.RunningEntry != NotStarted)
21573  {
21574  continue;
21575  }
21576  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
21577  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
21578  bool TrainOperatingFlag = false;
21579  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
21580  {
21581  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
21582  {
21583  TrainOperatingFlag = true;
21584  break;
21585  }
21586  }
21587  if(TrainOperatingFlag)
21588  {
21589  continue;
21590  }
21591  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
21592  {
21593  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
21594  }
21595  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
21596  {
21597  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
21598  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
21599  {
21600  break; // all the rest will also be greater (& default of -1 will be less)
21601  }
21602  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
21603  {
21604  break; // all the rest will also be greater (& default of -1 will be less)
21605  }
21606  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
21607  {
21608  break; // all the rest will also be greater (& default of -1 will be less)
21609  }
21610  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
21611  {
21612  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
21614  }
21615 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
21616  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
21617  {
21618  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
21619  NotStartedTrainArrDep++;
21620  }
21621 */
21622  }
21623  }
21624  }
21625  Utilities->CallLogPop(1894);
21626 }
21627 
21628 // ---------------------------------------------------------------------------
21629 
21631 // new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2.13.0)
21632 // clears entries then adds values for running trains then for continuation entries
21633 // dont limit size here as need to check all trains (ActionsDueListBox is limited to 20 trains in Interface.cpp)
21634 {
21635  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
21636  OpTimeToActMultiMap.clear();
21637  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
21638 
21639  if(!TrainVector.empty())
21640  // build OpTimeToActMultiMap entries for running trains
21641  {
21642  AnsiString HeadCode;
21643  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
21644  int TrainID;
21645  THCandTrainPosParam HCandTrainPosParam;
21646  for(unsigned int x = 0; x < TrainVector.size(); x++)
21647  {
21648  HeadCode = TrainVectorAt(62, x).HeadCode;
21649  TrainID = TrainVectorAt(63, x).TrainID;
21650  HCandTrainPosParam.first = HeadCode;
21651  HCandTrainPosParam.second = TrainID;
21652  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
21653  if((TimeToAct >= 0) && (TimeToAct < 59.9))
21654  // -1 indicates don't display
21655  {
21656  OpTimeToActMultiMapEntry.first = TimeToAct;
21657  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
21658  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
21659  }
21660  }
21661  }
21662 /*
21663  * class TContinuationTrainExpectationEntry
21664  {
21665  public:
21666  AnsiString Description; ///< service description
21667  AnsiString HeadCode; ///< service headcode
21668  int RepeatNumber; ///< service RepeatNumber
21669  int IncrementalMinutes; ///< Repeat separation in minutes
21670  int IncrementalDigits; ///< Repeat headcode separation
21671  int VectorPosition; ///< TrackVectorPosition for the continuation element
21672  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
21673  };
21674 
21675  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
21676  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
21677  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
21678  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
21679 */
21680 
21682  // build OpTimeToActMultiMap entries for expected trains
21683  {
21684  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
21685  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
21686  float TimeToAct = 0; // minutes
21687  int DistanceToRedSignal = 0; // metres
21688  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
21689  // used to ensure only one train displayed for a given continuation
21690  ContinuationEntryVecPosVector.clear();
21691  bool LaterTrain = false;
21694  {
21695  LaterTrain = false;
21696  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
21697  {
21698  CTEIt++;
21699  continue; // not interested in running or exited trains
21700  }
21701  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
21702  {
21703  CTEIt++;
21704  continue;
21705  // don't include trains not entered yet when a train is already on the continuation
21706  }
21707  if(!ContinuationEntryVecPosVector.empty())
21708  {
21709  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
21710  {
21711  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
21712  {
21713  LaterTrain = true;
21714  ;
21715  // skip past remaining trains waiting to enter at same point
21716  break;
21717  }
21718  }
21719  }
21720  if(LaterTrain)
21721  {
21722  CTEIt++;
21723  continue;
21724  }
21725  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
21726  AnsiString HeadCode = CTEIt->second.HeadCode;
21727  float CurrentStopTime; // set to 0 at start of function
21728  float LaterStopTime; // set to 0 at start of function
21729  float RecoverableTime; // set to 0 at start of function
21730  int AvTrackSpeed; // set to 0 at start of function
21731  int TrainID = -1; // not yet allocated for train still to enter
21732  int DistanceToExit; //not used for continuation entries
21733  THVShortPair ExitPair;
21734  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
21735 
21736 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
21737 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
21738 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
21739 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
21740 
21741  int AtValue = 1;
21742  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
21743  {
21744  AtValue = 0;
21745  }
21746  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
21747  // EntryPos always 0 for entering at a continuation
21748  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
21749  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
21750  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
21751  // for a train it's the one in front of LeadElement
21752  if(AvTrackSpeed < 30)
21753  {
21754  AvTrackSpeed = 30;
21755  }
21756  if(DistanceToRedSignal == -1)
21757  {
21758  TimeToAct = 60.0;
21759  }
21760  else
21761  {
21762  int Speed = AvTrackSpeed;
21763  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
21764  if(AvTrackSpeed > MaxSpeed)
21765  {
21766  Speed = MaxSpeed;
21767  }
21768  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
21769  // defined in timetable as under signaller control
21770  {
21771  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
21772  LaterStopTime = 0;
21773  }
21774  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
21775  // accel & decel taken into account in
21776  // CalcDistanceToRedSignalandStopTime
21777  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
21778  // don't need CurrentStopTime or RecoverableTime for continuation entries
21779  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
21780  TimeToAct += MinsBefEnter;
21781  }
21782  THCandTrainPosParam HCandTrainPosParam;
21783  HCandTrainPosParam.first = HeadCode;
21784  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
21785  // -1-CTE... because 2nd value covers TrainID if +ve &
21786  // continuation track vector position if -ve, -1 allows for vecpos being 0
21787  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
21788  {
21789  OpTimeToActMultiMapEntry.first = TimeToAct;
21790  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
21791  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
21792  }
21793  CTEIt++;
21794  }
21795  }
21796  Utilities->CallLogPop(2081);
21797 }
21798 
21799 // ---------------------------------------------------------------------------
21800 
21802 // new for multiplayer
21803 // clears entries then adds values for running trains
21804 {
21805  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
21806  TimeToExitMultiMap.clear();
21807  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
21808 
21809  if(!TrainVector.empty())
21810  // build map entries for running trains
21811  {
21812  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
21813  THVShortPair ExitPair;
21814  float TimeToExit;
21815  for(unsigned int x = 0; x < TrainVector.size(); x++)
21816  {
21818  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
21819  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
21820  ExitPair = TrainVectorAt(76, x).ExitPair;
21821  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
21822  {
21823  ExitInfo.TimeToExitSecs = -1;
21824  }
21825  TimeToExitMultiMapEntry.first = ExitPair;
21826  TimeToExitMultiMapEntry.second = ExitInfo;
21827  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
21828  }
21829  }
21830  Utilities->CallLogPop(2323);
21831 }
21832 
21833 // ---------------------------------------------------------------------------
21834 
21835 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
21836  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
21837  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
21838 // new v2.2.0
21839 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
21840 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
21841 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
21842 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
21843 // aren't used - this means there is no display for the train in question
21844 {
21845  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
21846  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
21847  int DistanceToRedSignal = 0;
21848  DistanceToExit = -1;
21849  ExitPair.first = -1;
21850  ExitPair.second = -1;
21851  int CumTrackSpeed = 0;
21852  // average track speed, in case need to use in time calc
21853  int TrackSpeedCount = 0;
21854  float KmPerLocationStop;
21855  float MaxAllowableSpeed;
21856 
21857  //below added at v2.6.1
21858  if(TrainID > -1) //will be -1 for trains not entered yet
21859  {
21860  TTrain &Train = TrainVectorAtIdent(51, TrainID);
21861  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
21862  Train.StationStopCalculated = false;
21863  }
21864  AvTrackSpeed = 0;
21865  int CurrentElement = TrackVectorPosition;
21866  int CurrentEntryPos = TrackVectorPositionEntryPos;
21867  int NextElement;
21868  int NextEntryPos;
21869  int NextExitPos;
21870 
21871  CurrentStopTime = 0;
21872  LaterStopTime = 0;
21873  RecoverableTime = 0;
21874  if(CurrentElement == -1) // train on end element, no action needed
21875  {
21876  Utilities->CallLogPop(2094);
21877  return(-1);
21878  }
21879  int CurrentExitPos;
21880 
21881  // get ExitPos for first element to be measured
21882  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
21883  {
21884  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
21885  {
21886  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
21887  {
21888  CurrentExitPos = 1;
21889  }
21890  else
21891  {
21892  CurrentExitPos = 3;
21893  }
21894  }
21895  else
21896  {
21897  CurrentExitPos = 0; // trailing point
21898  }
21899  }
21900  else
21901  {
21902  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
21903  }
21904  // get CumTrackSpeed for first measured element
21905 
21906  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
21907  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
21908  bool CurrentElementFailed = Track->TrackElementAt(1549, CurrentElement).Failed; //added at v2.13.2
21909 
21910  // check if currently stopped at a location, and if so add the remaining dwell time
21911  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
21912  if(TrainID > -1)
21913  // -1 for a continuation and can't be at a location as not yet entered
21914  {
21915  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
21917  // this used to deduct from RecoverableTime when arrive at a location
21918  if(Train.RevisedStoppedAtLoc())
21919  {
21920  if(Train.StoppedForTrainInFront || Train.TrainInFront)
21921  {
21922  Utilities->CallLogPop(2082);
21923  return(-1); // no action needed
21924  }
21926  { //added '|| (Train.ActionVectorEntryPtr->FormatType == TimeCmdDescription)' at v2.16.1 so description ignored in calculating action due time
21927  Utilities->CallLogPop(2083);
21928  return(-1); // not due a departure or a description change so no action needed
21929  }
21930  else if(((Train.ActionVectorEntryPtr + 1)->FormatType == TimeLoc) && (Train.ActionVectorEntryPtr->FormatType == TimeCmdDescription)) // due a departure immediately after change of description
21931  { //added at v2.16.1 to cover description change due next then a departure
21932  double TimeToDepart = double((Train.GetTrainTime(68, (Train.ActionVectorEntryPtr + 1)->DepartureTime)) - TrainController->TTClockTime) * 86400 / 60; // mins to depart excluding possible 30sec allowance from LastActionTime
21933  //need repeat time for the above
21934  if((Train.ActionVectorEntryPtr + 1)->DepartureTime == Train.ActionVectorEntryPtr->EventTime) //don't need repeat time here
21935  {
21936  TimeToDepart+= 0.5; //add in the 30 secs if depature time same as description change time
21937  }
21938  if(TimeToDepart < 0.5)
21939  {
21940  TimeToDepart = 0.5;
21941  }
21942  // can't convert a TDateTime to a float directly
21943  CurrentStopTime = float(TimeToDepart);
21944  AVPtr++;
21945  AVPtr++;
21946  }
21947  else if((Train.ActionVectorEntryPtr->FormatType == TimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc)) // due a departure as next action
21948  {
21949  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
21950  // can't convert a TDateTime to a float directly
21951  CurrentStopTime = float(TimeToDepart);
21952  AVPtr++;
21953  }
21954  else //added at v2.16.1 to catch all other combinations
21955  { //none of the above so no action needed
21956  Utilities->CallLogPop(2628);
21957  return(-1);
21958  }
21959  }
21960  }
21961  // check if CurrentElement is a red signal, but ok if autosig route after provided signal not failed
21962  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
21963  // ok if autosig route after red signal unless signal has failed
21964  {
21965  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
21966  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
21967  int RouteNumber; // holder for referenced value, not used
21968  if((AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
21969  { //CurrentElementFailed added at v2.13.2
21970  Utilities->CallLogPop(2078);
21971  return(-1);
21972  }
21973  else if(SigControlAndCanPassRedSignal)
21974  // ignore signal and increment CurrentElement to NextElement
21975  {
21976  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
21977  {
21978  if((NextEntryPos == 0) || (NextEntryPos == 2))
21979  // leading entry point
21980  {
21981  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
21982  {
21983  NextExitPos = 1;
21984  }
21985  else
21986  {
21987  NextExitPos = 3;
21988  }
21989  }
21990  else
21991  {
21992  NextExitPos = 0; // trailing entry point
21993  }
21994  }
21995  else
21996  {
21997  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
21998  }
21999  CurrentElement = NextElement;
22000  CurrentEntryPos = NextEntryPos;
22001  CurrentExitPos = NextExitPos;
22002  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
22003  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
22004  }
22005  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
22006  // give 'NOW' indication after allowed to pass stop signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
22007  {
22008  Utilities->CallLogPop(2084);
22009  return(0);
22010  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
22011  }
22012  }
22013  int LaterStopNumber = 0;
22014  int x = 0;
22015  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
22016 
22017  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
22018  // not red signal next (in fwd direction) so enter loop to calc CumLength
22019  {
22020  x++; // added in v2.4.0 as above
22021  if(x > 5000)
22022  {
22023  Utilities->CallLogPop(2120);
22024  return(-1);
22025  }
22026  if(CurrentEntryPos > 1)
22027  {
22028  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
22029  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
22030  }
22031  else
22032  {
22033  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
22034  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
22035  }
22036  TrackSpeedCount++;
22037 
22038  //added for multiplayer - exiting at a continuation and continuation length already added
22039  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
22040  {
22041  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
22042  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
22043  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
22044  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
22045  //need here as next element will be -1 so will exit before calcs at end
22046  if(TrackSpeedCount > 0)
22047  {
22048  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
22049  }
22050  else // shouldn't reach here but include to prevent divide by zero error
22051  {
22052  if(CurrentEntryPos > 1)
22053  {
22054  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
22055  }
22056  else
22057  {
22058  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
22059  }
22060  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
22061  }
22062  //calc AvTrackSpeed
22063  if(LaterStopNumber > 0)
22064  {
22065  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
22066  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
22067  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
22068  // average line speed/2 (for half distance accelerating and half decelerating.
22069  }
22070  else
22071  {
22072  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
22073  // using linear trendline for accel & decel distance at various speeds
22074  // at half braking, speed never < 60 using this
22075  }
22076  if(AvTrackSpeed > MaxAllowableSpeed)
22077  {
22078  AvTrackSpeed = MaxAllowableSpeed;
22079  }
22080  }
22081 
22082  // added at v2.6.1 to find DistanceToStationStop for trains running early
22083  if(TrainID > -1) //can ignore continuation entries as these don't run early
22084  {
22085  TTrain &Train = TrainVectorAtIdent(52, TrainID);
22086  if(!Train.StationStopCalculated)
22087  {
22088  if(Train.TrainMode == Timetable)
22089  {
22090  bool StopRequired = false;
22091  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
22092  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
22093  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos) ||
22094  (Track->TrackElementAt(1654, CurrentElement).StationEntryStopLinkPos3 == CurrentEntryPos) ||
22095  (Track->TrackElementAt(1655, CurrentElement).StationEntryStopLinkPos4 == CurrentEntryPos)))
22096  {
22097  // no need to add in the length of element to CumulativeLength
22098  if(StopRequired)
22099  {
22100  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
22101  Train.StationStopCalculated = true; //don't want to update it with later stops
22102  }
22103  }
22104  }
22105  }
22106  }
22107  // check for train in front, but if on a bridge on other track then ok
22108  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
22109  int TrainOnElement;
22110  if(TE.TrackType != Bridge)
22111  {
22112  TrainOnElement = TE.TrainIDOnElement;
22113  }
22114  else
22115  {
22116  if(CurrentEntryPos > 1)
22117  {
22119  }
22120  else
22121  {
22123  }
22124  }
22125  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
22126  // train in front before red signal
22127  {
22128  Utilities->CallLogPop(2085);
22129  return(-1);
22130  }
22131  // add to stoptime if required
22132  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
22133  {
22134  double StopTimeDouble;
22135  while(AVPtr->FormatType == PassTime)
22136  {
22137  AVPtr++; // skip past any passes
22138  }
22139  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
22140  (AVPtr->FormatType == TimeTimeLoc)))
22141  // stop due here so calc dwell time & advance Ptr
22142  {
22143  if(AVPtr->FormatType == TimeTimeLoc)
22144  {
22145  LaterStopNumber++;
22146  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
22147  if(StopTimeDouble < 0.5)
22148  {
22149  StopTimeDouble = 0.5;
22150  }
22151  // at least 30 secs delay at station
22152  // can't convert a TDateTime to a float directly
22153  LaterStopTime += float(StopTimeDouble);
22154  RecoverableTime += StopTimeDouble - 0.5;
22155  if((LaterStopNumber == 1) && (TrainID > -1))
22156  {
22157  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22158  }
22159  AVPtr++;
22160  }
22161  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
22162  {
22163  if((AVPtr + 1)->FormatType == TimeLoc)
22164  // must be a departure
22165  {
22166  LaterStopNumber++;
22167  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0; //diff will be same for all repeats
22168  // can't convert a TDateTime to a float directly //so repeat times not required
22169  if(TrainID > -1) //exclude trains still to enter
22170  {
22171  TTrain &Train = TrainVectorAtIdent(67, TrainID);
22172  if(TTClockTime > Train.GetTrainTime(69, AVPtr->ArrivalTime)) //running late, added at v2.16.1
22173  {
22174  StopTimeDouble = double(Train.GetTrainTime(70, (AVPtr + 1)->DepartureTime) - TTClockTime) * 86400.0 / 60.0; //may be -ve but if so it's set to 0.5 later
22175  // can't convert a TDateTime to a float directly
22176  }
22177  }
22178  if(StopTimeDouble < 0.5)
22179  {
22180  StopTimeDouble = 0.5;
22181  }
22182  // at least 30 secs delay at station
22183  LaterStopTime += float(StopTimeDouble);
22184  RecoverableTime += StopTimeDouble - 0.5;
22185  if((LaterStopNumber == 1) && (TrainID > -1))
22186  {
22187  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22188  }
22189  AVPtr++;
22190  AVPtr++;
22191  }
22192  else if(((AVPtr + 1)->FormatType == TimeCmdDescription) && ((AVPtr + 2)->FormatType == TimeLoc)) //change of description then departure
22193  { //added at v2.16.1 so description changes ignored in calculating time to act
22194  LaterStopNumber++;
22195  StopTimeDouble = double((AVPtr + 2)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0; //diff will be same for all repeats
22196  // can't convert a TDateTime to a float directly //so repeat times not required
22197  if(TrainID > -1) //exclude trains still to enter
22198  {
22199  TTrain &Train = TrainVectorAtIdent(68, TrainID);
22200  if(TTClockTime > Train.GetTrainTime(71, AVPtr->ArrivalTime)) //running late, added at v2.16.1
22201  {
22202  StopTimeDouble = double(Train.GetTrainTime(72, (AVPtr + 2)->DepartureTime) - TTClockTime) * 86400.0 / 60.0; //may be -ve but if so it's set to 0.5 later
22203  // can't convert a TDateTime to a float directly
22204  }
22205  }
22206  if(StopTimeDouble < 0.5)
22207  {
22208  StopTimeDouble = 0.5;
22209  }
22210  // at least 30 secs delay at station
22211  LaterStopTime += float(StopTimeDouble);
22212  RecoverableTime += StopTimeDouble - 0.5;
22213  if((LaterStopNumber == 1) && (TrainID > -1))
22214  {
22215  TrainVectorAtIdent(69, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
22216  }
22217  AVPtr++;
22218  AVPtr++;
22219  AVPtr++;
22220  }
22221  else // does something else at the location so no calculation needed
22222  {
22223  Utilities->CallLogPop(2086);
22224  return(-1);
22225  }
22226  }
22227  }
22228  }
22229  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
22230  if(NextElement == -1) // reached end element, no action needed
22231  {
22232  Utilities->CallLogPop(2077);
22233  return(-1);
22234  }
22235  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
22236  // get NextExitPos
22237  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
22238  {
22239  if((NextEntryPos == 0) || (NextEntryPos == 2))
22240  // leading entry point
22241  {
22242  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
22243  {
22244  NextExitPos = 1;
22245  }
22246  else
22247  {
22248  NextExitPos = 3;
22249  }
22250  }
22251  else
22252  {
22253  NextExitPos = 0; // trailing entry point
22254  }
22255  }
22256  else
22257  {
22258  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
22259  }
22260  CurrentElement = NextElement;
22261  CurrentEntryPos = NextEntryPos;
22262  CurrentExitPos = NextExitPos;
22263  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
22264  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
22265  CurrentElementFailed = Track->TrackElementAt(1550, CurrentElement).Failed; //added at v2.13.2
22266  }
22267  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
22268  // ok if autosig route after red signal, no action needed
22269  {
22270  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
22271  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
22272  int RouteNumber; // holder for referenced value, not used
22273  if((AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute) && !CurrentElementFailed)
22274  { //CurrentElementFailed added at v2.13.2
22275  Utilities->CallLogPop(2095);
22276  return(-1);
22277  }
22278  }
22279 
22280  if(TrackSpeedCount > 0)
22281  {
22282  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
22283  }
22284  else // shouldn't reach here but include to prevent divide by zero error
22285  {
22286  if(CurrentEntryPos > 1)
22287  {
22288  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
22289  }
22290  else
22291  {
22292  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
22293  }
22294  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
22295  }
22296 
22297  if(LaterStopNumber > 0)
22298  {
22299  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
22300  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
22301  }
22302  else
22303  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
22304  // average line speed/2 (for half distance accelerating and half decelerating.
22305  {
22306  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
22307  // using linear trendline for accel & decel distance at various speeds
22308  // at half braking, speed never < 60 using this
22309  }
22310  if(AvTrackSpeed > MaxAllowableSpeed)
22311  {
22312  AvTrackSpeed = MaxAllowableSpeed;
22313  }
22314  Utilities->CallLogPop(2096);
22315  return(DistanceToRedSignal);
22316 }
22317 
22318 // ---------------------------------------------------------------------------
22319 // end of TTrainController entries
22320 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:9170
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:19495
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:133
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:383
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:51
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:16012
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:351
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:350
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:496
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:710
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:72
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1742
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:904
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:174
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:15674
TTrainDataEntry::FixedDescription
AnsiString FixedDescription
Definition: TrainUnit.h:209
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6231
TActionVectorEntry::SplitDistribution
AnsiString SplitDistribution
Definition: TrainUnit.h:121
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:371
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel (OperatorActionPanel changed for ActionsDueForm at v2....
Definition: TrainUnit.cpp:21630
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:820
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:17116
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:92
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:52
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:1017
Create
@ Create
Definition: TrainUnit.h:51
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:797
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:20502
Arrive
@ Arrive
Definition: TrainUnit.h:51
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:51
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:349
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:389
Depart
@ Depart
Definition: TrainUnit.h:51
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:721
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:12280
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:719
PerfLogForm
TPerfLogForm * PerfLogForm
Definition: PerfLogUnit.cpp:11
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:292
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:433
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:804
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:287
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7480
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:52
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:40
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:910
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1671
IntermediateSequence
@ IntermediateSequence
Definition: TrainUnit.h:77
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:730
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:139
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:786
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1683
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:67
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:66
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:513
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:758
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:67
TTrain::CoastingBrakeRate
double CoastingBrakeRate
the train brake rate when coasting
Definition: TrainUnit.h:441
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:437
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:71
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:21263
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:14907
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Definition: TrackUnit.h:153
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:371
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:1003
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:417
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
TTrack::TTrackVectorIterator
std::vector< TTrackElement >::iterator TTrackVectorIterator
iterator for TTrackVector
Definition: TrackUnit.h:652
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:986
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:40
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:806
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:345
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:982
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:20918
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:8046
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:391
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:485
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:371
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6368
TTrackElement::StationEntryStopLinkPos3
int StationEntryStopLinkPos3
Definition: TrackUnit.h:153
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:806
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:10455
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:323
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:901
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route
Definition: TrainUnit.h:732
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:16847
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:107
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:864
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:1009
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:1016
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:185
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:20291
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:806
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:12230
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1663
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:9142
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:95
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:42
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:133
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:969
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:41
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:754
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:16862
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:267
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:967
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6151
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:42
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:983
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString HeadCode, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Return the TrainDataVector entry corresponding to ServiceReference, FinishType is 0 for end of servic...
Definition: TrainUnit.cpp:20274
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3593
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:692
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:66
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:509
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:467
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:10417
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:53
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:502
NotStarted
@ NotStarted
Definition: TrainUnit.h:88
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:11513
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:858
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:800
TTrack::OneStationLongEnoughForSplit
bool OneStationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10987
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:352
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:724
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:377
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:207
LeadMid
@ LeadMid
Definition: TrainUnit.h:300
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:41
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:300
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:848
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:20776
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:15758
TTrack::ResetAllTrainIDsAndFailedPointOrigSpeedLimits
void ResetAllTrainIDsAndFailedPointOrigSpeedLimits(int Caller)
Definition: TrackUnit.cpp:7852
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:21801
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:981
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6406
TOneRoute::RouteID
int RouteID
the ID number of the route, this is needed for session saves
Definition: TrackUnit.h:1558
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:750
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1023
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:254
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:1019
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:217
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:868
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:825
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:740
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:333
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:9619
ChangeDescription
@ ChangeDescription
Definition: TrainUnit.h:54
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:16965
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:39
MidLag
@ MidLag
Definition: TrainUnit.h:300
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:905
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:16903
StartNew
@ StartNew
Definition: TrainUnit.h:66
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:51
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:300
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:808
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:353
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:52
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:455
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:732
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:1011
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:20039
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:11754
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:992
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5960
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:516
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:186
TTrain
Definition: TrainUnit.h:306
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:989
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:11773
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:299
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:12249
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:42
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:900
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:252
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:882
TPerfLogForm::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: PerfLogUnit.cpp:32
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:491
GapJump
@ GapJump
Definition: TrackUnit.h:66
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:1010
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:851
NoSequence
@ NoSequence
Definition: TrainUnit.h:77
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:293
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:151
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:151
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:413
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6102
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:10048
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:371
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1710
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:371
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:295
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:365
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:227
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:713
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:369
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:63
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:16944
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:844
TTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:329
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:175
TTrain::TrainInFront
bool TrainInFront
Definition: TrainUnit.h:492
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:795
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:40
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:471
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:106
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:728
TTrain::LastSigPassedFailed
bool LastSigPassedFailed
flag used to erase route elements in an autosigs route after a failed signal
Definition: TrainUnit.h:399
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:16667
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1416
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6248
End
@ End
Definition: TrackUnit.h:76
TUtilities::FixedMinRepairTime
int FixedMinRepairTime
Definition: Utilities.h:72
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:403
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:7050
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:9054
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:347
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10952
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:67
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:305
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:409
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:17027
FailMissedDSC
@ FailMissedDSC
Definition: TrainUnit.h:40
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:125
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:977
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:41
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1027
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:304
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:856
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:845
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:427
SignalPost
@ SignalPost
Definition: TrackUnit.h:66
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:971
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:475
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:144
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1687
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:355
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:41
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:12923
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:999
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:209
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5730
TUtilities::MinorDelayFactor
float MinorDelayFactor
Definition: Utilities.h:53
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:298
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool SetDataAndCheckLocations, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:16035
TTrainController::CheckFourthValidityForSplit
bool CheckFourthValidityForSplit(AnsiString SplitDistributionString, bool GiveMessages)
Checks fourth segment in timetable for train splits - percentage mass then '-' then percentage power ...
Definition: TrainUnit.cpp:12185
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:147
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:859
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:781
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:909
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:425
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:75
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:487
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:65
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:854
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:820
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:898
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1927
TExitInfo
Definition: TrainUnit.h:103
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:291
TUtilities::ModerateDelayCutoff
float ModerateDelayCutoff
Definition: Utilities.h:51
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:491
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:39
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:852
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:305
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:987
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:296
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:66
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:94
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:133
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:491
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:17843
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:965
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:465
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:990
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:11017
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:752
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:371
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:463
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:81
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:1015
Pass
@ Pass
Definition: TrainUnit.h:53
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:379
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:91
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:806
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:766
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:43
EnRoute
@ EnRoute
Definition: TrainUnit.h:72
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3264
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11961
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:836
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:1002
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:500
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:784
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:890
TUtilities::ModerateDelayFactor
float ModerateDelayFactor
Definition: Utilities.h:54
TUtilities::MajorDelayCutoff
float MajorDelayCutoff
Definition: Utilities.h:52
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:975
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:726
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:359
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
Definition: TrainUnit.h:321
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:1008
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:831
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:9154
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:1011
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
< true if a description is given for the train, if only headcode given for a follow-on service then f...
Definition: TrainUnit.h:213
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:66
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:86
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:223
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6899
TTrainController::MTBFHours
double MTBFHours
Mean time between train failures in timetable clock hours.
Definition: TrainUnit.h:823
TTrack::TInfrastructureFailureEntry::TVPos
int TVPos
Definition: TrackUnit.h:715
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:19076
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:479
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:16304
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:313
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3472
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1022
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:473
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5280
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2785
RearSplit
@ RearSplit
Definition: TrainUnit.h:51
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:131
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:53
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:828
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:676
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:972
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:829
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:995
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:135
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:55
TUtilities::FailureMode
TFailureMode FailureMode
specifies whether no failures or minor, moderate or major random failures are to be applied (added at...
Definition: Utilities.h:119
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:3196
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6871
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:16402
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:53
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:894
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:165
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:850
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:874
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:385
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3543
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:968
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:9218
TUtilities::SignalChangeEventsPerFailure
int SignalChangeEventsPerFailure
number of signal changes between failures - reciprocal of failure probability per change
Definition: Utilities.h:95
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:337
TTrack::TInfrastructureFailureEntry
Definition: TrackUnit.h:714
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8964
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:8243
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:3188
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:835
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:496
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:40
TTrainController::TrainDataVectorCopy
TTrainDataVector TrainDataVectorCopy
vector containing the internal timetable, the copy is used for conflict analysis only
Definition: TrainUnit.h:888
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:469
NewService
@ NewService
Definition: TrainUnit.h:51
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:41
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:17180
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1518
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:491
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:387
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1670
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:375
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:225
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:221
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:842
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:302
TUtilities::CumulativeDelayedRandMinsAllTrains
int CumulativeDelayedRandMinsAllTrains
the running total of all random delays including knock-on delays for all trains, used to reduce total...
Definition: Utilities.h:101
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:170
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:1012
Crossover
@ Crossover
Definition: TrackUnit.h:66
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4880
AtLocation
@ AtLocation
Definition: TrainUnit.h:72
Signal
@ Signal
Definition: TrackUnit.h:76
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:974
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:1005
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:796
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:19732
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
Definition: TrainUnit.h:888
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:20553
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1671
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:471
Exited
@ Exited
Definition: TrainUnit.h:88
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10938
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:335
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:16878
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:8530
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:19558
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1024
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2403
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:491
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:59
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1692
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:964
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:491
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:764
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:315
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:808
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:51
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:435
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:820
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:994
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:970
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:371
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:66
TTrain::NewDelay
double NewDelay
an additional random delay at a location (added at v2.13.0)
Definition: TrainUnit.h:447
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:1006
Leave
@ Leave
Definition: TrainUnit.h:51
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:861
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:776
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:804
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel (OperatorA...
Definition: TrainUnit.h:461
TTrack::FailedSignalsVector
TFailedElementVector FailedSignalsVector
Definition: TrackUnit.h:794
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:401
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:3169
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:973
FNil
@ FNil
Definition: Utilities.h:43
NoFormat
@ NoFormat
Definition: TrainUnit.h:66
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:816
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:42
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:42
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:41
TTrainController::ControllerGetNewServiceDepartureInfo
AnsiString ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, int IncrementalDigits, AnsiString RetStr)
Similar to TTrain::GetNewServiceDepartureInfo for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:10650
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:66
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2714
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:459
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:491
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3572
Enter
@ Enter
Definition: TrainUnit.h:51
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:518
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:8067
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:980
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:16927
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:453
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:278
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:15582
TActionVectorEntry::NewDescription
AnsiString NewDescription
Definition: TrainUnit.h:121
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:756
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6314
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:9654
Timetable
@ Timetable
Definition: TrainUnit.h:60
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:15967
StartSequence
@ StartSequence
Definition: TrainUnit.h:77
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6958
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:814
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11812
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:786
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:129
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:19404
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:115
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:806
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:275
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:21522
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:82
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:443
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:774
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:265
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:812
TTrack::InactiveTrackVector
TTrackVector InactiveTrackVector
Definition: TrackUnit.h:828
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, AnsiString SplitDistribution, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5387
Terminate
@ Terminate
Definition: TrainUnit.h:51
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:820
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:800
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:491
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:149
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:507
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:373
Running
@ Running
Definition: TrainUnit.h:88
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:287
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3669
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:899
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:14879
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:16440
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:17203
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:463
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:12032
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:728
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:241
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:862
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:429
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2600
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:20415
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:287
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0 for train failures, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:878
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1657
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:10137
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:9707
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6783
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:872
TTrain::GetNewServiceDepartureInfo
AnsiString GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr, bool TimetableTime)
called during FloatingLabelNextString to find the next service departure time & next location (last b...
Definition: TrainUnit.cpp:7582
TTrain::Description
AnsiString Description
needs own HeadCode & description because repeat entries will differ from TrainDataEntry values (Descr...
Definition: TrainUnit.h:329
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:997
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:128
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:520
TUtilities::DelayMode
TDelayMode DelayMode
specifies whether no delays or minor, moderate or major random delays are to be applied (added at v2....
Definition: Utilities.h:117
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:9718
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:1014
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:477
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1744
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:820
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:12321
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3668
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3368
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:471
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:21835
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:808
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:72
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:53
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1655
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:9111
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:3232
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:119
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:838
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:17049
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:268
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:991
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:16381
TDisplay
Definition: DisplayUnit.h:49
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:147
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:880
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:492
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4817
TimeCmdDescription
@ TimeCmdDescription
Definition: TrainUnit.h:67
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:280
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:38
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in Actions...
Definition: TrainUnit.cpp:10471
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:52
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:361
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:411
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:12421
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:834
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:97
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:9732
TTrackElement::Failed
bool Failed
New parameter added at v2.13.0 for failed points, signals & TSRs.
Definition: TrackUnit.h:141
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:155
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:857
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:906
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:451
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:750
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:160
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1725
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:215
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:130
TUtilities::MinorDelayCutoff
float MinorDelayCutoff
Definition: Utilities.h:50
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:113
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:996
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:229
TTrain::DelayedRandMins
double DelayedRandMins
the remaining random delay at any point in time for the train (added at v2.13.0)
Definition: TrainUnit.h:445
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:149
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:827
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:910
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:560
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5303
TTrain::TRSTime
TDateTime TRSTime
Definition: TrainUnit.h:473
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route (this will be truncated...
Definition: TrackUnit.h:1661
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:40
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:107
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1671
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:137
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:341
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:1020
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:719
Moderate
@ Moderate
Definition: Utilities.h:38
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:717
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:301
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:10121
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:897
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:843
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:39
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:357
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:17216
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:770
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:985
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:153
Major
@ Major
Definition: Utilities.h:38
Points
@ Points
Definition: TrackUnit.h:66
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:11826
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:743
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:397
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:837
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:15481
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:40
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:395
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:820
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:860
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:53
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3517
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:1004
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:39
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:405
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:268
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:511
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all signals appropriately. Also called when a new train is added a...
Definition: TrackUnit.cpp:18036
Continuation
@ Continuation
Definition: TrackUnit.h:66
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:806
TTrain::ActualArrivalTime
TDateTime ActualArrivalTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:473
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7825
GraphicUnit.h
PerfLogUnit.h
TUtilities::MajorDelayFactor
float MajorDelayFactor
Definition: Utilities.h:55
TTrack::PointFlashFlag
bool PointFlashFlag
< true if a route set through an LC that is closed to trains (& therefore needs to be opened)
Definition: TrackUnit.h:768
TTrack::ThisLocationLongEnoughForSplit
bool ThisLocationLongEnoughForSplit(int Caller, AnsiString HeadCode, int TrainID, AnsiString LocationName, int LeadElement, int LeadExitPos, int MidElement, int MidEntryPos, int &FrontTrainFrontPos, int &FrontTrainRearPos, int &RearTrainFrontPos, int &RearTrainRearPos, bool &TemporaryDelay)
checks if the track that the train is on is long enough for a split, returns false if not,...
Definition: TrackUnit.cpp:11258
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:853
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:483
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:10889
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:55
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:367
NoEvent
@ NoEvent
Definition: TrainUnit.h:39
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:39
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:21026
TTrack::TInfrastructureFailureEntry::FailureTime
TDateTime FailureTime
Definition: TrackUnit.h:716
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3326
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:902
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:53
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:787
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:905
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:1018
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:10502
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:88
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:331
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:489
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:686
Minor
@ Minor
Definition: Utilities.h:38
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:742
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6598
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:135
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:855
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:498
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:269
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:180
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:42
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrainDataEntry::ExplicitDescription
bool ExplicitDescription
< headcode is the first train's headcode, rest are calculated from repeat information; ServiceReferen...
Definition: TrainUnit.h:211
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:518
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:53
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:1001
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:21093
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:526
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:54
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:904
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:993
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:317
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:287
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:710
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:20167
TTrack::TInfrastructureFailureEntry::RepairTime
TDateTime RepairTime
Definition: TrackUnit.h:717
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:728
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:9074
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:65
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:806
TTrain::NextTrainID
static int NextTrainID
< km/h
Definition: TrainUnit.h:326
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:773
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:187
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:726
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:988
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:20189
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:20145
TTrain::CumulativeDelayedRandMinsOneTrain
double CumulativeDelayedRandMinsOneTrain
the running total of all random delays including knock-on delays for a single train,...
Definition: TrainUnit.h:449
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:810
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:121
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:284
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:42
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:40
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit01
Definition: TrackUnit.h:155
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:909
TTrackElement::StationEntryStopLinkPos4
int StationEntryStopLinkPos4
Used for track at platforms ( 1 & 2) and non-station named locations (1 - 4) to mark the train front ...
Definition: TrackUnit.h:153
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:339
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:50
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:728
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:371
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:847
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1025
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:82
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:10088
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:16328
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7798
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:19417
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6655
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:421
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:40
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5783
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:20311
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:10106
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:145
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:14886
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:348
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:17009
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:185
TUtilities::MaxRandomRepairTime
int MaxRandomRepairTime
Definition: Utilities.h:71
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:155
NoLocation
@ NoLocation
Definition: TrainUnit.h:72
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool SetDataAndCheckLocations, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:15007
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:936
TTrainController::MinsToAnsiTime
AnsiString MinsToAnsiTime(int Input)
converts an integer minute value to string "HH:MM" added at v2.15.0
Definition: TrainUnit.cpp:16413
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:393
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:39
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:219
NoMode
@ NoMode
Definition: TrainUnit.h:60
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:849
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:12771
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:100
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:777
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:820
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:66
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:176
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:201
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:870
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:841
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:818
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:523
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:491
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:10490
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:984
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:143
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:299
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:43
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:381
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:789
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:105
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:242
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:16001
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:294
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7564
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:457
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:715
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1665
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:141
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:363
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:779
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:978
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:1021
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:775
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3290
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
Definition: TrainUnit.h:121
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:143
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:415
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:105
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:1013
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:121
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6709
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:5012
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
< service description
Definition: TrainUnit.h:748
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:151
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:407
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:866
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:820
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:976
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6588
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:728
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:66
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:52
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:778
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:21476
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:114
THVShortPair
std::pair< int, int > THVShortPair
Definition: InterfaceUnit.h:82
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:744
TTrainController::TContinuationTrainExpectationEntry::FixedDescription
AnsiString FixedDescription
Definition: TrainUnit.h:746
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:66
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:833
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:1007
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:884
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:730
TActionVectorEntry::Reminder
unsigned int Reminder
Definition: TrainUnit.h:149
TActionVectorEntry::SignallerControl
bool SignallerControl
< string values for timetabled event entries, null on creation
Definition: TrainUnit.h:125
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5292
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:439
TUtilities::LastDelayTTClockTime
double LastDelayTTClockTime
Clock time at which the latest delay for any train occurred. Used to prevent new delays within 5 minu...
Definition: Utilities.h:85
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:151
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:734
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:343
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:756
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:732
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:67
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:846
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:3214
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag = 1, top = 2, top rh diag = 3,...
Definition: TrackUnit.h:90
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:652
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1074
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:998
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:20595
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6626
TTrackElement::TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
int TrainIDOnBridgeOrFailedPointOrigSpeedLimit23
Definition: TrackUnit.h:155
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3710
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning and perf log panels when the action is reached
Definition: TrainUnit.h:127
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2802
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:303
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:190
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:209
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6989
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:346
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:41
NotSet
@ NotSet
Definition: TrackUnit.h:76
Repeat
@ Repeat
Definition: TrainUnit.h:67
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:966
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:14895
FinishSequence
@ FinishSequence
Definition: TrainUnit.h:77
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:678
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:75
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:820
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int TrackPos, int LinkPos, int OwnTrainID)
True if another train on LinkPos track of element at TrackPos, whether bridge or not,...
Definition: TrackUnit.cpp:11670
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:161
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:67
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:150
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:492
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:1000
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:750
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:895
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:250
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5943
TAllRoutes::TLockedRouteClass::RearTrackVectorPosition
unsigned int RearTrackVectorPosition
the TrackVector position of the rearmost element selected for truncation (this will be truncated)
Definition: TrackUnit.h:1659
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:20337
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:145
Signaller
@ Signaller
Definition: TrainUnit.h:60
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:504
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:907
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:183
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:39
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:895
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:419
Bridge
@ Bridge
Definition: TrackUnit.h:66
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:979
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:282
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:371
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:76
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:431
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:77
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:528
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:37
Buffers
@ Buffers
Definition: TrackUnit.h:66
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:131
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:297
TTrack::OneNonStationLongEnoughForSplit
bool OneNonStationLongEnoughForSplit(int Caller, AnsiString LocationName)
As below but here allow points & crossovers.
Definition: TrackUnit.cpp:11094
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:121